From 56f572048850fc61644311539a92ff4f8df69787 Mon Sep 17 00:00:00 2001 From: Juan David Hurtado Date: Wed, 1 Jun 2022 17:23:13 -0500 Subject: [PATCH 1/4] feat: add tab header ui --- .../PuraceDemo.xcodeproj/project.pbxproj | 28 +++++++-- .../Examples/{ => Basic}/ButtonExample.swift | 0 .../Examples/{ => Basic}/ImageExample.swift | 0 .../Examples/{ => Basic}/LoaderExample.swift | 0 .../Examples/{ => Basic}/TextExample.swift | 0 .../{ => Complex}/CollectionCardExample.swift | 0 .../Examples/{ => Complex}/GridExample.swift | 0 .../{ => Complex}/SnackbarExample.swift | 0 .../Examples/{ => Complex}/StoryExample.swift | 0 .../Examples/Complex/TabExample.swift | 17 ++++++ PuraceDemo/PuraceDemo/MenuView.swift | 3 + .../Views/Complex/Tab/PuraceTabItem.swift | 14 +++++ .../Views/Complex/Tab/PuraceTabView.swift | 58 +++++++++++++++++++ 13 files changed, 116 insertions(+), 4 deletions(-) rename PuraceDemo/PuraceDemo/Examples/{ => Basic}/ButtonExample.swift (100%) rename PuraceDemo/PuraceDemo/Examples/{ => Basic}/ImageExample.swift (100%) rename PuraceDemo/PuraceDemo/Examples/{ => Basic}/LoaderExample.swift (100%) rename PuraceDemo/PuraceDemo/Examples/{ => Basic}/TextExample.swift (100%) rename PuraceDemo/PuraceDemo/Examples/{ => Complex}/CollectionCardExample.swift (100%) rename PuraceDemo/PuraceDemo/Examples/{ => Complex}/GridExample.swift (100%) rename PuraceDemo/PuraceDemo/Examples/{ => Complex}/SnackbarExample.swift (100%) rename PuraceDemo/PuraceDemo/Examples/{ => Complex}/StoryExample.swift (100%) create mode 100644 PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift create mode 100644 Sources/Purace/Views/Complex/Tab/PuraceTabItem.swift create mode 100644 Sources/Purace/Views/Complex/Tab/PuraceTabView.swift diff --git a/PuraceDemo/PuraceDemo.xcodeproj/project.pbxproj b/PuraceDemo/PuraceDemo.xcodeproj/project.pbxproj index 5f04a99..fe84405 100644 --- a/PuraceDemo/PuraceDemo.xcodeproj/project.pbxproj +++ b/PuraceDemo/PuraceDemo.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 2F1F7D7128317E0700AA30DB /* CollectionCardExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1F7D7028317E0700AA30DB /* CollectionCardExample.swift */; }; 2F1F7D7328318D9900AA30DB /* TextExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1F7D7228318D9900AA30DB /* TextExample.swift */; }; 2F1F7D7528318EEE00AA30DB /* ButtonExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1F7D7428318EEE00AA30DB /* ButtonExample.swift */; }; + 2F27BC9D284814B600B5AC8D /* TabExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F27BC9C284814B600B5AC8D /* TabExample.swift */; }; 2F77A234283C4AC700F143FB /* ImageExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F77A233283C4AC700F143FB /* ImageExample.swift */; }; 2F9321B3282EE49E003DA929 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F9321B2282EE49E003DA929 /* AppDelegate.swift */; }; 2F9321B5282EE49E003DA929 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F9321B4282EE49E003DA929 /* SceneDelegate.swift */; }; @@ -29,6 +30,7 @@ 2F1F7D7028317E0700AA30DB /* CollectionCardExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionCardExample.swift; sourceTree = ""; }; 2F1F7D7228318D9900AA30DB /* TextExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextExample.swift; sourceTree = ""; }; 2F1F7D7428318EEE00AA30DB /* ButtonExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonExample.swift; sourceTree = ""; }; + 2F27BC9C284814B600B5AC8D /* TabExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabExample.swift; sourceTree = ""; }; 2F77A233283C4AC700F143FB /* ImageExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageExample.swift; sourceTree = ""; }; 2F9321AF282EE49E003DA929 /* PuraceDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PuraceDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 2F9321B2282EE49E003DA929 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -58,16 +60,33 @@ 2F1F7D6F28317DE200AA30DB /* Examples */ = { isa = PBXGroup; children = ( - 2F1F7D7028317E0700AA30DB /* CollectionCardExample.swift */, + 2F27BC9B284814A700B5AC8D /* Complex */, + 2F27BC9A2848149900B5AC8D /* Basic */, + ); + path = Examples; + sourceTree = ""; + }; + 2F27BC9A2848149900B5AC8D /* Basic */ = { + isa = PBXGroup; + children = ( 2F1F7D7228318D9900AA30DB /* TextExample.swift */, 2F1F7D7428318EEE00AA30DB /* ButtonExample.swift */, + 2F072906283C00080098C5AF /* LoaderExample.swift */, + 2F77A233283C4AC700F143FB /* ImageExample.swift */, + ); + path = Basic; + sourceTree = ""; + }; + 2F27BC9B284814A700B5AC8D /* Complex */ = { + isa = PBXGroup; + children = ( + 2F1F7D7028317E0700AA30DB /* CollectionCardExample.swift */, 2FC004082831AC250037EF10 /* StoryExample.swift */, 2FA986782837351B000DB409 /* GridExample.swift */, 2FA9867A28373DD3000DB409 /* SnackbarExample.swift */, - 2F072906283C00080098C5AF /* LoaderExample.swift */, - 2F77A233283C4AC700F143FB /* ImageExample.swift */, + 2F27BC9C284814B600B5AC8D /* TabExample.swift */, ); - path = Examples; + path = Complex; sourceTree = ""; }; 2F9321A6282EE49E003DA929 = { @@ -195,6 +214,7 @@ 2F1F7D7528318EEE00AA30DB /* ButtonExample.swift in Sources */, 2FA986792837351B000DB409 /* GridExample.swift in Sources */, 2F9321B5282EE49E003DA929 /* SceneDelegate.swift in Sources */, + 2F27BC9D284814B600B5AC8D /* TabExample.swift in Sources */, 2F072907283C00080098C5AF /* LoaderExample.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/PuraceDemo/PuraceDemo/Examples/ButtonExample.swift b/PuraceDemo/PuraceDemo/Examples/Basic/ButtonExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/ButtonExample.swift rename to PuraceDemo/PuraceDemo/Examples/Basic/ButtonExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/ImageExample.swift b/PuraceDemo/PuraceDemo/Examples/Basic/ImageExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/ImageExample.swift rename to PuraceDemo/PuraceDemo/Examples/Basic/ImageExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/LoaderExample.swift b/PuraceDemo/PuraceDemo/Examples/Basic/LoaderExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/LoaderExample.swift rename to PuraceDemo/PuraceDemo/Examples/Basic/LoaderExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/TextExample.swift b/PuraceDemo/PuraceDemo/Examples/Basic/TextExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/TextExample.swift rename to PuraceDemo/PuraceDemo/Examples/Basic/TextExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/CollectionCardExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/CollectionCardExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/CollectionCardExample.swift rename to PuraceDemo/PuraceDemo/Examples/Complex/CollectionCardExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/GridExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/GridExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/GridExample.swift rename to PuraceDemo/PuraceDemo/Examples/Complex/GridExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/SnackbarExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/SnackbarExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/SnackbarExample.swift rename to PuraceDemo/PuraceDemo/Examples/Complex/SnackbarExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/StoryExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/StoryExample.swift similarity index 100% rename from PuraceDemo/PuraceDemo/Examples/StoryExample.swift rename to PuraceDemo/PuraceDemo/Examples/Complex/StoryExample.swift diff --git a/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift new file mode 100644 index 0000000..bcdffbf --- /dev/null +++ b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift @@ -0,0 +1,17 @@ +// +// TabExample.swift +// PuraceDemo +// +// Created by Juan Hurtado on 1/06/22. +// + +import Foundation +import SwiftUI +import Purace + +struct TabExample: View { + var body: some View { + PuraceTabView() + Spacer() + } +} diff --git a/PuraceDemo/PuraceDemo/MenuView.swift b/PuraceDemo/PuraceDemo/MenuView.swift index 14d4d01..4a2c6b8 100644 --- a/PuraceDemo/PuraceDemo/MenuView.swift +++ b/PuraceDemo/PuraceDemo/MenuView.swift @@ -40,6 +40,9 @@ struct MenuView: View { NavigationLink("Grid") { GridExample() } + NavigationLink("Tab") { + TabExample() + } } } .navigationTitle("Purace") diff --git a/Sources/Purace/Views/Complex/Tab/PuraceTabItem.swift b/Sources/Purace/Views/Complex/Tab/PuraceTabItem.swift new file mode 100644 index 0000000..04beeb1 --- /dev/null +++ b/Sources/Purace/Views/Complex/Tab/PuraceTabItem.swift @@ -0,0 +1,14 @@ +// +// PuraceTabItem.swift +// +// +// Created by Juan Hurtado on 1/06/22. +// + +import Foundation +import SwiftUI + +struct PuraceTabItem { + let title: String + let content: () -> T +} diff --git a/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift new file mode 100644 index 0000000..f3c3771 --- /dev/null +++ b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift @@ -0,0 +1,58 @@ +// +// PuraceTabView.swift +// +// +// Created by Juan Hurtado on 1/06/22. +// + +import Foundation +import SwiftUI + +public struct PuraceTabView: View { + let titles: [String] = ["Acerca de", "Imágenes", "Ubicación"] + @State var selectedIndex = 0 + @State var indicatorOffset: CGFloat = .zero + + public init() {} + + func headers(in size: CGSize) -> some View { + HStack(spacing: 0) { + ForEach(0.. some View { + ZStack { + Color.black + .frame(height: 1) + .opacity(0.1) + HStack { + PuraceStyle.Color.B2 + .frame(width: size.width / CGFloat(titles.count), height: 3) + Spacer(minLength: 0) + }.offset(x: indicatorOffset, y: 0) + } + } + + func updateIndicatorOffset(size: CGSize) { + withAnimation(.linear(duration: 0.2)) { + indicatorOffset = CGFloat(selectedIndex) * size.width / CGFloat(titles.count) + } + } + + public var body: some View { + GeometryReader { reader in + VStack(spacing: 0) { + headers(in: reader.size) + indicator(in: reader.size) + } + } + } +} From 0b94afe3c3b16e2a296e28cda9ac87032c7f3ac9 Mon Sep 17 00:00:00 2001 From: Juan David Hurtado Date: Wed, 1 Jun 2022 17:33:04 -0500 Subject: [PATCH 2/4] feat: add tabs content view --- .../PuraceDemo/Examples/Complex/TabExample.swift | 13 ++++++++++++- .../Purace/Views/Complex/Tab/PuraceTabView.swift | 8 ++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift index bcdffbf..5d56865 100644 --- a/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift +++ b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift @@ -11,7 +11,18 @@ import Purace struct TabExample: View { var body: some View { - PuraceTabView() + PuraceTabView { index in + Group { + switch index { + case 0: + PuraceTextView("First view") + case 1: + PuraceTextView("Second view") + default: + PuraceTextView("Third view") + } + }.frame(height: 100) + } Spacer() } } diff --git a/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift index f3c3771..bcd35f6 100644 --- a/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift +++ b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift @@ -8,12 +8,15 @@ import Foundation import SwiftUI -public struct PuraceTabView: View { +public struct PuraceTabView: View { let titles: [String] = ["Acerca de", "Imágenes", "Ubicación"] @State var selectedIndex = 0 @State var indicatorOffset: CGFloat = .zero + var viewForIndex: (Int) -> T - public init() {} + public init(viewForIndex: @escaping (Int) -> T) { + self.viewForIndex = viewForIndex + } func headers(in size: CGSize) -> some View { HStack(spacing: 0) { @@ -52,6 +55,7 @@ public struct PuraceTabView: View { VStack(spacing: 0) { headers(in: reader.size) indicator(in: reader.size) + viewForIndex(selectedIndex) } } } From 69a79c59a3911aa516757294ad56fc4566831981 Mon Sep 17 00:00:00 2001 From: Juan David Hurtado Date: Thu, 2 Jun 2022 16:06:46 -0500 Subject: [PATCH 3/4] refactor: use z-stack for tab elements --- .../Examples/Complex/TabExample.swift | 46 +++++++++++++++++-- .../Views/Complex/Tab/PuraceTabView.swift | 10 +++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift index 5d56865..980b617 100644 --- a/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift +++ b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift @@ -7,21 +7,57 @@ import Foundation import SwiftUI +import MapKit import Purace +struct IdentifiablePlace: Identifiable { + let id: UUID + let location: CLLocationCoordinate2D + init(id: UUID = UUID(), lat: Double, long: Double) { + self.id = id + self.location = CLLocationCoordinate2D( + latitude: lat, + longitude: long) + } +} + struct TabExample: View { + let place = IdentifiablePlace(lat: 2.443881, long: -76.605059) + @State private var region = MKCoordinateRegion( + center: CLLocationCoordinate2D(latitude: 2.443881, + longitude: -76.605059), + latitudinalMeters: 750, + longitudinalMeters: 750 + ) + var body: some View { PuraceTabView { index in Group { switch index { case 0: - PuraceTextView("First view") - case 1: - PuraceTextView("Second view") + VStack { + Map(coordinateRegion: $region, annotationItems: [place]) { item in + MapMarker(coordinate: place.location, tint: .black) + }.frame(height: UIScreen.main.bounds.height * 0.5) + Spacer(minLength: 0) + } default: - PuraceTextView("Third view") + ScrollView { + PuraceVerticalGridView(columns: 2, spacing: 1) { + PuraceImageView(url: URL(string: "https://www.biografiasyvidas.com/biografia/c/fotos/caldas_francisco_jose_2.jpg")) + .frame(height: 200) + PuraceImageView(url: URL(string: "https://www.biografiasyvidas.com/biografia/c/fotos/caldas_francisco_jose_2.jpg")) + .frame(height: 200) + PuraceImageView(url: URL(string: "https://www.biografiasyvidas.com/biografia/c/fotos/caldas_francisco_jose_2.jpg")) + .frame(height: 200) + PuraceImageView(url: URL(string: "https://www.biografiasyvidas.com/biografia/c/fotos/caldas_francisco_jose_2.jpg")) + .frame(height: 200) + } + } } - }.frame(height: 100) + }.frame(height: UIScreen.main.bounds.height * 0.7) + }.onAppear { + MKMapView.appearance().mapType = .mutedStandard } Spacer() } diff --git a/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift index bcd35f6..7216cb6 100644 --- a/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift +++ b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI public struct PuraceTabView: View { - let titles: [String] = ["Acerca de", "Imágenes", "Ubicación"] + let titles: [String] = ["Acerca de", "Imágenes"] @State var selectedIndex = 0 @State var indicatorOffset: CGFloat = .zero var viewForIndex: (Int) -> T @@ -55,7 +55,13 @@ public struct PuraceTabView: View { VStack(spacing: 0) { headers(in: reader.size) indicator(in: reader.size) - viewForIndex(selectedIndex) + ZStack { + ForEach(0.. Date: Thu, 2 Jun 2022 16:09:46 -0500 Subject: [PATCH 4/4] refactor: set tab titles as parameter --- PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift | 2 +- Sources/Purace/Views/Complex/Tab/PuraceTabView.swift | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift index 980b617..c974326 100644 --- a/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift +++ b/PuraceDemo/PuraceDemo/Examples/Complex/TabExample.swift @@ -31,7 +31,7 @@ struct TabExample: View { ) var body: some View { - PuraceTabView { index in + PuraceTabView(titles: ["Acerca de", "Imágenes"]) { index in Group { switch index { case 0: diff --git a/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift index 7216cb6..e859f1b 100644 --- a/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift +++ b/Sources/Purace/Views/Complex/Tab/PuraceTabView.swift @@ -9,12 +9,13 @@ import Foundation import SwiftUI public struct PuraceTabView: View { - let titles: [String] = ["Acerca de", "Imágenes"] + let titles: [String] @State var selectedIndex = 0 @State var indicatorOffset: CGFloat = .zero var viewForIndex: (Int) -> T - public init(viewForIndex: @escaping (Int) -> T) { + public init(titles: [String], viewForIndex: @escaping (Int) -> T) { + self.titles = titles self.viewForIndex = viewForIndex }