├── SwiftUIDemos ├── Assets.xcassets │ ├── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Example Views │ ├── Tagger │ │ ├── SubViews │ │ │ ├── ErrorMessageView.swift │ │ │ ├── TagView.swift │ │ │ ├── TagListView.swift │ │ │ └── TagEntryView.swift │ │ └── TaggerView.swift │ ├── Toast │ │ ├── ToastExampleView.swift │ │ └── SubViews │ │ │ └── ToastOverlayView.swift │ └── FilterGroupedList │ │ ├── DummyListData.swift │ │ ├── SubViews │ │ ├── SearchBarView.swift │ │ ├── PeopleListView.swift │ │ └── DepartmentListView.swift │ │ └── GroupedListView.swift ├── SwiftUIDemosApp.swift └── Info.plist ├── SwiftUIDemos.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── alexhay.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj └── README.md /SwiftUIDemos/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftUIDemos/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftUIDemos.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftUIDemos/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SwiftUIDemos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftUIDemos.xcodeproj/xcuserdata/alexhay.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftUIDemos.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/Tagger/SubViews/ErrorMessageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorMessageView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// A subview that displays a message when an error occurs 11 | struct ErrorMessageView: View { 12 | 13 | @Binding var showingError: Bool 14 | @Binding var errorString: String 15 | 16 | var body: some View { 17 | HStack { 18 | Image(systemName: "exclamationmark.triangle.fill") 19 | .foregroundColor(.orange) 20 | Text(errorString) 21 | .foregroundColor(.secondary) 22 | .padding(.leading, -6) 23 | } 24 | .font(.caption) 25 | .opacity(showingError ? 1 : 0) 26 | .animation(.easeIn(duration: 0.3)) 27 | } 28 | } 29 | 30 | struct ErrorMessageView_Previews: PreviewProvider { 31 | static var previews: some View { 32 | ErrorMessageView(showingError: .constant(true), errorString: .constant("Tag can't be a duplicate")) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/Tagger/SubViews/TagView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// A subview of a tag shown in a list. When tapped the tag will be removed from the array 11 | struct TagView: View { 12 | 13 | var tag: String 14 | @Binding var tags: [String] 15 | 16 | var body: some View { 17 | HStack { 18 | Text(tag.lowercased()) 19 | .padding(.leading, 2) 20 | Image(systemName: "xmark.circle.fill") 21 | .opacity(0.4) 22 | .padding(.leading, -6) 23 | } 24 | .foregroundColor(.white) 25 | .font(.caption2) 26 | .padding(4) 27 | .background(Color.blue.cornerRadius(5)) 28 | .padding(4) 29 | .onTapGesture { 30 | tags = tags.filter({ $0 != tag }) 31 | } 32 | } 33 | } 34 | 35 | struct TagView_Previews: PreviewProvider { 36 | static var previews: some View { 37 | TagView(tag: "hello world", tags: .constant(["tag one", "tag two"])) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SwiftUIDemos/SwiftUIDemosApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIDemosApp.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct SwiftUIDemosApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | IndexView() 15 | } 16 | } 17 | } 18 | 19 | struct IndexView: View { 20 | 21 | var body: some View { 22 | NavigationView { 23 | List { 24 | NavigationLink( 25 | destination: TaggerView(), 26 | label: { 27 | Image(systemName: "tag") 28 | Text("Tagger") 29 | } 30 | ) 31 | NavigationLink( 32 | destination: GroupedListView(), 33 | label: { 34 | Image(systemName: "rectangle.grid.1x2") 35 | Text("Grouped List") 36 | } 37 | ) 38 | NavigationLink( 39 | destination: ToastExampleView(), 40 | label: { 41 | Image(systemName: "rectangle.bottomthird.inset.fill") 42 | Text("Toast") 43 | } 44 | ) 45 | } 46 | .listStyle(InsetGroupedListStyle()) 47 | .navigationBarTitle(Text("Example Views")) 48 | } 49 | } 50 | } 51 | 52 | struct IndexView_Previews: PreviewProvider { 53 | static var previews: some View { 54 | IndexView() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/Toast/ToastExampleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToastExampleView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ToastExampleView: View { 11 | 12 | @State var textToCopy = "hello world" 13 | @State var showingToast = false 14 | 15 | var body: some View { 16 | VStack(alignment: .center) { 17 | TextField("Text To Copy", text: $textToCopy) 18 | .textFieldStyle(RoundedBorderTextFieldStyle()) 19 | Button(action: { 20 | UIPasteboard.general.string = textToCopy 21 | showingToast = true 22 | }, label: { 23 | Text("Copy Text") 24 | .foregroundColor(.white) 25 | .padding(.horizontal) 26 | .padding(.vertical, 7) 27 | .background(Color.blue.cornerRadius(8)) 28 | .padding() 29 | }) 30 | Spacer() 31 | } 32 | .padding() 33 | .toast(isPresented: $showingToast) { 34 | HStack { 35 | Image(systemName: "checkmark.circle") 36 | Text("Copied To Clipboard") 37 | } 38 | } 39 | .navigationBarTitle(Text("Toast")) 40 | } 41 | } 42 | 43 | struct ToastExampleView_Previews: PreviewProvider { 44 | static var previews: some View { 45 | ToastExampleView() 46 | } 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/FilterGroupedList/DummyListData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Countries+PeopleModel.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: DUMMY DATA 11 | class DummyData { 12 | static let shared = DummyData() 13 | var names: [String] = ["Alex","Emma","Simon","Jane","Paul", "John", "Jane", "Mary", "Jack", "Linda"] 14 | var people: [Person] = [] 15 | private init() { 16 | people = makePeople(withNames: names) 17 | } 18 | } 19 | 20 | // Generates some randomised dummy data from the array of names when run 21 | extension DummyData { 22 | func makePeople(withNames names: [String]) -> [Person] { 23 | return names.map({ Person(name: $0, department: Departments.allCases.randomElement() ?? .design) }) 24 | } 25 | } 26 | 27 | // MARK: DUMMY DATA MODEL 28 | struct Person: Identifiable { 29 | let id: UUID = UUID() 30 | let name: String 31 | let department: Departments 32 | } 33 | 34 | enum Departments: String, CaseIterable { 35 | case development = "Development" 36 | case design = "Design" 37 | case sales = "Sales" 38 | case marketing = "Marketing" 39 | } 40 | 41 | // Allows the departments enum to be sorted by their raw value 42 | extension Departments: Comparable { 43 | static func < (lhs: Departments, rhs: Departments) -> Bool { lhs.rawValue < rhs.rawValue } 44 | } 45 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/Tagger/TaggerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TaggerView.swift 3 | // 4 | // Created by Alex Hay on 21/11/2020. 5 | // 6 | // Simple interface for adding tags to an array in SwiftUI 7 | // Example video: https://imgur.com/gallery/CcA1IXp 8 | // alignmentGuide code from Asperi @ https://stackoverflow.com/a/58876712/11685049 9 | 10 | import SwiftUI 11 | 12 | /// The main view to add tags to an array 13 | struct TaggerView: View { 14 | 15 | @State var newTag = "" 16 | @State var tags = ["example","hello world"] 17 | @State var showingError = false 18 | @State var errorString = "x" // Can't start with empty string or view will pop as size changes 19 | 20 | var body: some View { 21 | VStack(alignment: .leading) { 22 | ErrorMessageView(showingError: $showingError, errorString: $errorString) 23 | TagEntryView(newTag: $newTag, tags: $tags, showingError: $showingError, errorString: $errorString) 24 | TagListView(tags: $tags) 25 | } 26 | .padding() 27 | .onChange(of: showingError, perform: { value in 28 | if value { 29 | // Hide the error message after a delay 30 | DispatchQueue.main.asyncAfter(deadline: .now() + 3) { 31 | showingError = false 32 | } 33 | } 34 | }) 35 | .navigationBarTitle(Text("Tagger")) 36 | } 37 | } 38 | 39 | struct TaggerView_Previews: PreviewProvider { 40 | static var previews: some View { 41 | TaggerView() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/FilterGroupedList/SubViews/SearchBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | // MARK: SEARCH BAR 11 | /// Search box for filtering the list 12 | struct SearchBarView: View { 13 | 14 | @Binding var searchQuery: String 15 | 16 | var body: some View { 17 | 18 | HStack { 19 | Spacer() 20 | TextField("Search", text: $searchQuery) 21 | .modifier(ClearButtonModifier(text: $searchQuery)) 22 | .textFieldStyle(RoundedBorderTextFieldStyle()) 23 | .frame(minWidth: nil, idealWidth: nil, maxWidth: 240, minHeight: nil, idealHeight: nil, maxHeight: nil, alignment: .center) 24 | Spacer() 25 | } 26 | } 27 | } 28 | 29 | // MARK: CLEAR BUTTON 30 | /// Adds a button next to the search field that clears the search query when pressed 31 | public struct ClearButtonModifier: ViewModifier { 32 | 33 | @Binding var text: String 34 | 35 | public func body(content: Content) -> some View { 36 | HStack { 37 | content 38 | Image(systemName: "multiply.circle.fill") 39 | .foregroundColor(.secondary) 40 | .opacity(text == "" ? 0 : 0.5) 41 | .onTapGesture { 42 | text = "" 43 | } 44 | .animation(.easeInOut(duration: 0.2)) 45 | } 46 | } 47 | } 48 | 49 | struct SearchBarView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | SearchBarView(searchQuery: .constant("hello world")) 52 | SearchBarView(searchQuery: .constant("")) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI-Demos 2 | 3 | As I learn SwiftUI I'm adding generic interfaces I build to this repo as examples of different types of UIs that can be created and adapted. 4 | 5 | The project is built for for iOS14+ 6 | 7 | So far it includes: 8 | 9 | ## Tagger 10 | A simple interface for adding and removing tags to an array. It includes a useful tag list that dynamically flows elements onto new rows and error handling. 11 | 12 | ![A screenshot from an iPhone of an interface with a textfield with a plus button next to it and several blue tags below it](https://i.imgur.com/ewYkiz8.png "Tagger SwiftUI Interface") 13 | 14 | ## Grouped List 15 | An inset list that has a search field for filtering and a picker to choose between grouped & non-grouped list. Includes disclosure groups for hiding grouped content. 16 | 17 | ![A screenshot from an iPhone of an interface with a title that says "Personnel". Below the title is a picker between "Staff" and "Departments". Below this is a text field and a filtered list of staff, grouped by their department](https://i.imgur.com/rMEPGc1.png "Grouped List SwiftUI Interface") 18 | 19 | ## Toast 20 | A notification view that is temporarily displayed on top of the UI. 21 | 22 | ![A screenshot from an iPhone of an interface with a title that says "Toast". Below the title is a text field containing the words "hello world. Below that is a button that says copy text and at the bottom of the screen is a grey notification window that has a check mark and text saying "copied to clipboard"](https://i.imgur.com/sIOaSG7.png "Toast SwiftUI Interface") 23 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/Toast/SubViews/ToastOverlayView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToastOverlayView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 24/11/2020. 6 | // 7 | // Adapted from this StackOverflow answer: https://stackoverflow.com/a/56550601/11685049 8 | 9 | import SwiftUI 10 | 11 | /// A convenience method on View for presenting a toast 12 | extension View { 13 | func toast(isPresented: Binding, content: @escaping () -> Content) -> some View where Content: View { 14 | Toast(isPresented: isPresented, presenter: { self }, content: content) 15 | } 16 | } 17 | 18 | struct Toast: View where Presenting: View, Content: View { 19 | 20 | @Binding var isPresented: Bool 21 | let presenter: () -> Presenting // The view that will be "presenting" this toast 22 | let content: () -> Content // The toast overlay view 23 | let delay: TimeInterval = 1.5 // How long the toast will display before disappearing 24 | 25 | var body: some View { 26 | if isPresented { 27 | DispatchQueue.main.asyncAfter(deadline: .now() + delay) { 28 | withAnimation { 29 | isPresented = false 30 | } 31 | } 32 | } 33 | 34 | return GeometryReader { geometry in 35 | ZStack(alignment: .bottom) { 36 | presenter() 37 | ZStack { 38 | RoundedRectangle(cornerRadius: 15) 39 | .foregroundColor(Color(UIColor.tertiarySystemBackground)) 40 | .shadow(radius: 7) 41 | content() 42 | } 43 | .frame(width: geometry.size.width / 1.5, height: geometry.size.height / 13) 44 | .opacity(isPresented ? 1 : 0) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/Tagger/SubViews/TagListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagListView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// A subview containing a list of all tags that are in the array. Tags flow onto the next line when wider than the view's width 11 | struct TagListView: View { 12 | 13 | @Binding var tags: [String] 14 | 15 | var body: some View { 16 | GeometryReader { geo in 17 | generateTags(in: geo) 18 | .padding(.top) 19 | } 20 | } 21 | 22 | /// Adds a tag view for each tag in the array. Populates from left to right and then on to new rows when too wide for the screen 23 | private func generateTags(in geo: GeometryProxy) -> some View { 24 | var width: CGFloat = 0 25 | var height: CGFloat = 0 26 | 27 | return ZStack(alignment: .topLeading) { 28 | ForEach(tags, id: \.self) { tag in 29 | TagView(tag: tag, tags: $tags) 30 | .alignmentGuide(.leading, computeValue: { tagSize in 31 | if (abs(width - tagSize.width) > geo.size.width) { 32 | width = 0 33 | height -= tagSize.height 34 | } 35 | let offset = width 36 | if tag == tags.last ?? "" { 37 | width = 0 38 | } else { 39 | width -= tagSize.width 40 | } 41 | return offset 42 | }) 43 | .alignmentGuide(.top, computeValue: { tagSize in 44 | let offset = height 45 | if tag == tags.last ?? "" { 46 | height = 0 47 | } 48 | return offset 49 | }) 50 | } 51 | } 52 | } 53 | } 54 | struct TagListView_Previews: PreviewProvider { 55 | static var previews: some View { 56 | TagListView(tags: .constant(["tag one", "tag two"])) 57 | .padding() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/FilterGroupedList/SubViews/PeopleListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeopleListView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | // MARK: PEOPLE LIST 11 | struct PeopleListView: View { 12 | 13 | var people: [Person] 14 | var showDepartmentName: Bool 15 | 16 | var body: some View { 17 | ForEach(people, id: \.id) { person in 18 | Label( 19 | title: { 20 | VStack(alignment: .leading) { 21 | Text(person.name) 22 | if showDepartmentName { 23 | Text(person.department.rawValue) 24 | .foregroundColor(.secondary) 25 | .font(.caption) 26 | } 27 | } 28 | }, 29 | icon: { 30 | Image(systemName: getIconName(forDepartment: person.department)) 31 | .foregroundColor(.primary) 32 | } 33 | ) 34 | .padding(.vertical, 6) 35 | } 36 | } 37 | 38 | // Retrieves a different SFSymbol name for each department 39 | func getIconName(forDepartment department: Departments) -> String { 40 | switch department { 41 | case .design: 42 | return "paintbrush.pointed" 43 | case .development: 44 | return "curlybraces.square" 45 | case .marketing: 46 | return "bubble.left" 47 | case .sales: 48 | return "dollarsign.circle" 49 | } 50 | } 51 | } 52 | 53 | struct PeopleListView_Previews: PreviewProvider { 54 | static var previews: some View { 55 | List { 56 | PeopleListView(people: DummyData.shared.people, showDepartmentName: true) 57 | } 58 | .listStyle(InsetGroupedListStyle()) 59 | List { 60 | PeopleListView(people: DummyData.shared.people, showDepartmentName: false) 61 | } 62 | .listStyle(InsetGroupedListStyle()) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/FilterGroupedList/SubViews/DepartmentListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DepartmentListView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | // MARK: DEPARTMENT LIST 11 | struct DepartmentListView: View { 12 | 13 | @Binding var searchQuery: String 14 | 15 | var body: some View { 16 | ForEach(Departments.allCases.sorted(), id: \.self) { department in 17 | if let people = getPeople(fromDepartment: department), !people.isEmpty { 18 | DisclosureGroup( 19 | content: { 20 | PeopleListView(people: people, showDepartmentName: false) 21 | }, 22 | label: { 23 | HStack { 24 | Text(department.rawValue) 25 | Spacer() 26 | Text("\(people.count)") 27 | .foregroundColor(.secondary) 28 | } 29 | } 30 | ) 31 | } 32 | } 33 | } 34 | 35 | /// Gets all dummy people from the given department and filters them down to those with names containing the search query 36 | func getPeople(fromDepartment department: Departments) -> [Person] { 37 | let people: [Person] = DummyData.shared.people 38 | var results = [Person]() 39 | if searchQuery == "" { 40 | results = people.filter({ $0.department == department }) 41 | } else { 42 | let lowercaseQuery = searchQuery.lowercased() 43 | results = people.filter({ $0.department == department && $0.name.lowercased().contains(lowercaseQuery) }) 44 | } 45 | return results.sorted(by: {$0.name < $1.name}) 46 | } 47 | } 48 | 49 | struct DepartmentListView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | List { 52 | DepartmentListView(searchQuery: .constant("")) 53 | .padding() 54 | } 55 | .listStyle(InsetGroupedListStyle()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /SwiftUIDemos/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | 28 | UIApplicationSupportsIndirectInputEvents 29 | 30 | UILaunchScreen 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /SwiftUIDemos/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/FilterGroupedList/GroupedListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GroupedListView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | // Filterable groupd list with disclosure groups 8 | 9 | import SwiftUI 10 | 11 | // MARK: GROUPED LIST 12 | struct GroupedListView: View { 13 | 14 | @Environment(\.colorScheme) var colorScheme // Check whether the environment is .light or .dark 15 | @State private var pickerChoice: PickerChoice = .allStaff 16 | @State private var searchQuery: String = "" 17 | 18 | var body: some View { 19 | List { 20 | Section { 21 | // Choose between "Staff" or "Departments" list views 22 | Picker("", selection: $pickerChoice) { 23 | ForEach(PickerChoice.allCases, id: \.self) { value in 24 | Text(value.rawValue) 25 | } 26 | } 27 | // Hides the grouped list background 28 | .listRowBackground(colorScheme == .light ? Color(.systemGray6) : Color(.systemBackground)) 29 | .pickerStyle(SegmentedPickerStyle()) 30 | } 31 | Section { 32 | SearchBarView(searchQuery: $searchQuery) 33 | .listRowBackground(colorScheme == .light ? Color(.systemGray6) : Color(.systemBackground)) 34 | } 35 | Section { 36 | switch pickerChoice { 37 | case .allStaff: 38 | PeopleListView(people: filterPeople(), showDepartmentName: true) 39 | case .byDepartment: 40 | DepartmentListView(searchQuery: $searchQuery) 41 | } 42 | } 43 | } 44 | .listStyle(InsetGroupedListStyle()) 45 | .navigationTitle(Text("Personnel")) 46 | 47 | } 48 | 49 | enum PickerChoice: String, CaseIterable { 50 | case allStaff = "Staff" 51 | case byDepartment = "Departments" 52 | } 53 | 54 | /// Gets all dummy people and filters them down to those with names or departments containing the search query 55 | func filterPeople() -> [Person] { 56 | let allPeople = DummyData.shared.people 57 | if searchQuery == "" { 58 | return allPeople 59 | } else { 60 | let lowercaseQuery = searchQuery.lowercased() 61 | let filteredPeople = allPeople.filter({ 62 | $0.name.lowercased().contains(lowercaseQuery) || $0.department.rawValue.lowercased().contains(lowercaseQuery) 63 | }) 64 | return filteredPeople.sorted(by: {$0.name < $1.name}) 65 | } 66 | } 67 | } 68 | 69 | // MARK: PREVIEWS 70 | struct GroupedListView_Previews: PreviewProvider { 71 | static var previews: some View { 72 | GroupedListView() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /SwiftUIDemos/Example Views/Tagger/SubViews/TagEntryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagEntryView.swift 3 | // SwiftUIDemos 4 | // 5 | // Created by Alex Hay on 23/11/2020. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// A subview that contains the text-entry field for entering new tags 11 | struct TagEntryView: View { 12 | 13 | @Binding var newTag: String 14 | @Binding var tags: [String] 15 | @Binding var showingError: Bool 16 | @Binding var errorString: String 17 | 18 | var body: some View { 19 | HStack { 20 | TextField("Add Tags", text: $newTag, onCommit: { 21 | addTag(newTag) 22 | }) 23 | .textFieldStyle(RoundedBorderTextFieldStyle()) 24 | .autocapitalization(.none) 25 | Spacer() 26 | Image(systemName: "plus.circle") 27 | // Entry button is blue if the tag is a valid entry 28 | .foregroundColor((isTagValid(newTag) == .IsValid) ? .blue : .secondary) 29 | .onTapGesture { 30 | addTag(newTag) 31 | } 32 | } 33 | .onChange(of: newTag, perform: { value in 34 | if value.contains(",") { 35 | newTag = value.replacingOccurrences(of: ",", with: "") 36 | if value != "," { // Don't enter a tag if the user just types "," 37 | // Try to add the tag if user types a comma 38 | addTag(newTag) 39 | } 40 | } 41 | }) 42 | } 43 | 44 | /// Checks if the entered text is valid as a tag 45 | private func isTagValid(_ tag: String) -> ErrorCode { 46 | // Invalid tags: 47 | // - empty strings 48 | // - tags already in the tag array 49 | let lowerTag = tag.lowercased() 50 | if lowerTag == "" { 51 | return .Empty 52 | } else if tags.contains(lowerTag) { 53 | return .Duplicate 54 | } else { 55 | return .IsValid 56 | } 57 | } 58 | 59 | /// If the tag is valid, it is added to an array, otherwise the error message is shown 60 | private func addTag(_ tag: String) { 61 | let code = isTagValid(tag) 62 | switch code { 63 | case .Duplicate, .Empty: 64 | showError(code) 65 | case .IsValid: 66 | tags.append(newTag.lowercased()) 67 | newTag = "" 68 | } 69 | } 70 | 71 | /// Shows error text above the tag entry field 72 | private func showError(_ code: ErrorCode) { 73 | errorString = code.rawValue 74 | showingError = true 75 | } 76 | 77 | enum ErrorCode: String { 78 | case Empty = "Tag can't be empty" 79 | case Duplicate = "Tag can't be a duplicate" 80 | case IsValid = "" 81 | } 82 | } 83 | 84 | struct TagEntryView_Previews: PreviewProvider { 85 | static var previews: some View { 86 | VStack { 87 | TagEntryView(newTag: .constant(""), tags: .constant(["tag one", "tag two"]), showingError: .constant(false), errorString: .constant("Tag can't be a duplicate")) 88 | .padding() 89 | TagEntryView(newTag: .constant("hello world"), tags: .constant(["tag one", "tag two"]), showingError: .constant(true), errorString: .constant("Tag can't be a duplicate")) 90 | .padding() 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SwiftUIDemos.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8C2FB90E256C600600BD5CD9 /* DepartmentListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2FB90D256C600600BD5CD9 /* DepartmentListView.swift */; }; 11 | 8C2FB911256C608600BD5CD9 /* PeopleListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2FB910256C608600BD5CD9 /* PeopleListView.swift */; }; 12 | 8C2FB914256C616A00BD5CD9 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2FB913256C616A00BD5CD9 /* SearchBarView.swift */; }; 13 | 8C2FB91D256C828E00BD5CD9 /* ToastExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2FB91C256C828E00BD5CD9 /* ToastExampleView.swift */; }; 14 | 8C2FB922256C850500BD5CD9 /* ToastOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C2FB921256C850500BD5CD9 /* ToastOverlayView.swift */; }; 15 | 8CA247AF256BAE580006B376 /* SwiftUIDemosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247AE256BAE580006B376 /* SwiftUIDemosApp.swift */; }; 16 | 8CA247B3256BAE5B0006B376 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8CA247B2256BAE5B0006B376 /* Assets.xcassets */; }; 17 | 8CA247B6256BAE5B0006B376 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8CA247B5256BAE5B0006B376 /* Preview Assets.xcassets */; }; 18 | 8CA247BF256BAE6F0006B376 /* TaggerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247BE256BAE6F0006B376 /* TaggerView.swift */; }; 19 | 8CA247CA256BB6DD0006B376 /* ErrorMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247C9256BB6DD0006B376 /* ErrorMessageView.swift */; }; 20 | 8CA247CF256BB7890006B376 /* TagEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247CE256BB7890006B376 /* TagEntryView.swift */; }; 21 | 8CA247D2256BB8D60006B376 /* TagListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247D1256BB8D60006B376 /* TagListView.swift */; }; 22 | 8CA247D6256BB9300006B376 /* TagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247D5256BB9300006B376 /* TagView.swift */; }; 23 | 8CA247DB256C1D260006B376 /* GroupedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247DA256C1D260006B376 /* GroupedListView.swift */; }; 24 | 8CA247E0256C1D580006B376 /* DummyListData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA247DF256C1D580006B376 /* DummyListData.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 8C2FB90D256C600600BD5CD9 /* DepartmentListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepartmentListView.swift; sourceTree = ""; }; 29 | 8C2FB910256C608600BD5CD9 /* PeopleListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeopleListView.swift; sourceTree = ""; }; 30 | 8C2FB913256C616A00BD5CD9 /* SearchBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarView.swift; sourceTree = ""; }; 31 | 8C2FB91C256C828E00BD5CD9 /* ToastExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastExampleView.swift; sourceTree = ""; }; 32 | 8C2FB921256C850500BD5CD9 /* ToastOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastOverlayView.swift; sourceTree = ""; }; 33 | 8CA247AB256BAE580006B376 /* SwiftUIDemos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIDemos.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 8CA247AE256BAE580006B376 /* SwiftUIDemosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIDemosApp.swift; sourceTree = ""; }; 35 | 8CA247B2256BAE5B0006B376 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 36 | 8CA247B5256BAE5B0006B376 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 37 | 8CA247B7256BAE5B0006B376 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 8CA247BE256BAE6F0006B376 /* TaggerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaggerView.swift; sourceTree = ""; }; 39 | 8CA247C9256BB6DD0006B376 /* ErrorMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessageView.swift; sourceTree = ""; }; 40 | 8CA247CE256BB7890006B376 /* TagEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagEntryView.swift; sourceTree = ""; }; 41 | 8CA247D1256BB8D60006B376 /* TagListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagListView.swift; sourceTree = ""; }; 42 | 8CA247D5256BB9300006B376 /* TagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagView.swift; sourceTree = ""; }; 43 | 8CA247DA256C1D260006B376 /* GroupedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupedListView.swift; sourceTree = ""; }; 44 | 8CA247DF256C1D580006B376 /* DummyListData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyListData.swift; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 8CA247A8256BAE580006B376 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 8C2FB909256C2F0D00BD5CD9 /* SubViews */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 8C2FB90D256C600600BD5CD9 /* DepartmentListView.swift */, 62 | 8C2FB910256C608600BD5CD9 /* PeopleListView.swift */, 63 | 8C2FB913256C616A00BD5CD9 /* SearchBarView.swift */, 64 | ); 65 | path = SubViews; 66 | sourceTree = ""; 67 | }; 68 | 8C2FB919256C677000BD5CD9 /* Example Views */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 8C2FB91B256C826500BD5CD9 /* Toast */, 72 | 8CA247D9256C1CB80006B376 /* FilterGroupedList */, 73 | 8CA247C7256BB6B60006B376 /* Tagger */, 74 | ); 75 | path = "Example Views"; 76 | sourceTree = ""; 77 | }; 78 | 8C2FB91B256C826500BD5CD9 /* Toast */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 8C2FB91C256C828E00BD5CD9 /* ToastExampleView.swift */, 82 | 8C2FB920256C84F800BD5CD9 /* SubViews */, 83 | ); 84 | path = Toast; 85 | sourceTree = ""; 86 | }; 87 | 8C2FB920256C84F800BD5CD9 /* SubViews */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 8C2FB921256C850500BD5CD9 /* ToastOverlayView.swift */, 91 | ); 92 | path = SubViews; 93 | sourceTree = ""; 94 | }; 95 | 8CA247A2256BAE580006B376 = { 96 | isa = PBXGroup; 97 | children = ( 98 | 8CA247AD256BAE580006B376 /* SwiftUIDemos */, 99 | 8CA247AC256BAE580006B376 /* Products */, 100 | ); 101 | sourceTree = ""; 102 | }; 103 | 8CA247AC256BAE580006B376 /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 8CA247AB256BAE580006B376 /* SwiftUIDemos.app */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | 8CA247AD256BAE580006B376 /* SwiftUIDemos */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 8CA247AE256BAE580006B376 /* SwiftUIDemosApp.swift */, 115 | 8C2FB919256C677000BD5CD9 /* Example Views */, 116 | 8CA247B2256BAE5B0006B376 /* Assets.xcassets */, 117 | 8CA247B7256BAE5B0006B376 /* Info.plist */, 118 | 8CA247B4256BAE5B0006B376 /* Preview Content */, 119 | ); 120 | path = SwiftUIDemos; 121 | sourceTree = ""; 122 | }; 123 | 8CA247B4256BAE5B0006B376 /* Preview Content */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 8CA247B5256BAE5B0006B376 /* Preview Assets.xcassets */, 127 | ); 128 | path = "Preview Content"; 129 | sourceTree = ""; 130 | }; 131 | 8CA247C7256BB6B60006B376 /* Tagger */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 8CA247BE256BAE6F0006B376 /* TaggerView.swift */, 135 | 8CA247C8256BB6BF0006B376 /* SubViews */, 136 | ); 137 | path = Tagger; 138 | sourceTree = ""; 139 | }; 140 | 8CA247C8256BB6BF0006B376 /* SubViews */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 8CA247C9256BB6DD0006B376 /* ErrorMessageView.swift */, 144 | 8CA247CE256BB7890006B376 /* TagEntryView.swift */, 145 | 8CA247D1256BB8D60006B376 /* TagListView.swift */, 146 | 8CA247D5256BB9300006B376 /* TagView.swift */, 147 | ); 148 | path = SubViews; 149 | sourceTree = ""; 150 | }; 151 | 8CA247D9256C1CB80006B376 /* FilterGroupedList */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 8C2FB909256C2F0D00BD5CD9 /* SubViews */, 155 | 8CA247DF256C1D580006B376 /* DummyListData.swift */, 156 | 8CA247DA256C1D260006B376 /* GroupedListView.swift */, 157 | ); 158 | path = FilterGroupedList; 159 | sourceTree = ""; 160 | }; 161 | /* End PBXGroup section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | 8CA247AA256BAE580006B376 /* SwiftUIDemos */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = 8CA247BA256BAE5B0006B376 /* Build configuration list for PBXNativeTarget "SwiftUIDemos" */; 167 | buildPhases = ( 168 | 8CA247A7256BAE580006B376 /* Sources */, 169 | 8CA247A8256BAE580006B376 /* Frameworks */, 170 | 8CA247A9256BAE580006B376 /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = SwiftUIDemos; 177 | productName = SwiftUIDemos; 178 | productReference = 8CA247AB256BAE580006B376 /* SwiftUIDemos.app */; 179 | productType = "com.apple.product-type.application"; 180 | }; 181 | /* End PBXNativeTarget section */ 182 | 183 | /* Begin PBXProject section */ 184 | 8CA247A3256BAE580006B376 /* Project object */ = { 185 | isa = PBXProject; 186 | attributes = { 187 | LastSwiftUpdateCheck = 1220; 188 | LastUpgradeCheck = 1220; 189 | TargetAttributes = { 190 | 8CA247AA256BAE580006B376 = { 191 | CreatedOnToolsVersion = 12.2; 192 | }; 193 | }; 194 | }; 195 | buildConfigurationList = 8CA247A6256BAE580006B376 /* Build configuration list for PBXProject "SwiftUIDemos" */; 196 | compatibilityVersion = "Xcode 9.3"; 197 | developmentRegion = en; 198 | hasScannedForEncodings = 0; 199 | knownRegions = ( 200 | en, 201 | Base, 202 | ); 203 | mainGroup = 8CA247A2256BAE580006B376; 204 | productRefGroup = 8CA247AC256BAE580006B376 /* Products */; 205 | projectDirPath = ""; 206 | projectRoot = ""; 207 | targets = ( 208 | 8CA247AA256BAE580006B376 /* SwiftUIDemos */, 209 | ); 210 | }; 211 | /* End PBXProject section */ 212 | 213 | /* Begin PBXResourcesBuildPhase section */ 214 | 8CA247A9256BAE580006B376 /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 8CA247B6256BAE5B0006B376 /* Preview Assets.xcassets in Resources */, 219 | 8CA247B3256BAE5B0006B376 /* Assets.xcassets in Resources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | /* End PBXResourcesBuildPhase section */ 224 | 225 | /* Begin PBXSourcesBuildPhase section */ 226 | 8CA247A7256BAE580006B376 /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | 8CA247CF256BB7890006B376 /* TagEntryView.swift in Sources */, 231 | 8C2FB90E256C600600BD5CD9 /* DepartmentListView.swift in Sources */, 232 | 8CA247DB256C1D260006B376 /* GroupedListView.swift in Sources */, 233 | 8C2FB922256C850500BD5CD9 /* ToastOverlayView.swift in Sources */, 234 | 8CA247CA256BB6DD0006B376 /* ErrorMessageView.swift in Sources */, 235 | 8CA247D2256BB8D60006B376 /* TagListView.swift in Sources */, 236 | 8CA247BF256BAE6F0006B376 /* TaggerView.swift in Sources */, 237 | 8C2FB911256C608600BD5CD9 /* PeopleListView.swift in Sources */, 238 | 8C2FB914256C616A00BD5CD9 /* SearchBarView.swift in Sources */, 239 | 8CA247AF256BAE580006B376 /* SwiftUIDemosApp.swift in Sources */, 240 | 8CA247D6256BB9300006B376 /* TagView.swift in Sources */, 241 | 8CA247E0256C1D580006B376 /* DummyListData.swift in Sources */, 242 | 8C2FB91D256C828E00BD5CD9 /* ToastExampleView.swift in Sources */, 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | }; 246 | /* End PBXSourcesBuildPhase section */ 247 | 248 | /* Begin XCBuildConfiguration section */ 249 | 8CA247B8256BAE5B0006B376 /* Debug */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | ALWAYS_SEARCH_USER_PATHS = NO; 253 | CLANG_ANALYZER_NONNULL = YES; 254 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 255 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 256 | CLANG_CXX_LIBRARY = "libc++"; 257 | CLANG_ENABLE_MODULES = YES; 258 | CLANG_ENABLE_OBJC_ARC = YES; 259 | CLANG_ENABLE_OBJC_WEAK = YES; 260 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 261 | CLANG_WARN_BOOL_CONVERSION = YES; 262 | CLANG_WARN_COMMA = YES; 263 | CLANG_WARN_CONSTANT_CONVERSION = YES; 264 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 265 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 266 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 267 | CLANG_WARN_EMPTY_BODY = YES; 268 | CLANG_WARN_ENUM_CONVERSION = YES; 269 | CLANG_WARN_INFINITE_RECURSION = YES; 270 | CLANG_WARN_INT_CONVERSION = YES; 271 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 272 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 273 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 274 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 275 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 276 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 277 | CLANG_WARN_STRICT_PROTOTYPES = YES; 278 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 279 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | COPY_PHASE_STRIP = NO; 283 | DEBUG_INFORMATION_FORMAT = dwarf; 284 | ENABLE_STRICT_OBJC_MSGSEND = YES; 285 | ENABLE_TESTABILITY = YES; 286 | GCC_C_LANGUAGE_STANDARD = gnu11; 287 | GCC_DYNAMIC_NO_PIC = NO; 288 | GCC_NO_COMMON_BLOCKS = YES; 289 | GCC_OPTIMIZATION_LEVEL = 0; 290 | GCC_PREPROCESSOR_DEFINITIONS = ( 291 | "DEBUG=1", 292 | "$(inherited)", 293 | ); 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | IPHONEOS_DEPLOYMENT_TARGET = 14.2; 301 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 302 | MTL_FAST_MATH = YES; 303 | ONLY_ACTIVE_ARCH = YES; 304 | SDKROOT = iphoneos; 305 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 306 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 307 | }; 308 | name = Debug; 309 | }; 310 | 8CA247B9256BAE5B0006B376 /* Release */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_ANALYZER_NONNULL = YES; 315 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_ENABLE_OBJC_WEAK = YES; 321 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 322 | CLANG_WARN_BOOL_CONVERSION = YES; 323 | CLANG_WARN_COMMA = YES; 324 | CLANG_WARN_CONSTANT_CONVERSION = YES; 325 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 328 | CLANG_WARN_EMPTY_BODY = YES; 329 | CLANG_WARN_ENUM_CONVERSION = YES; 330 | CLANG_WARN_INFINITE_RECURSION = YES; 331 | CLANG_WARN_INT_CONVERSION = YES; 332 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 334 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 335 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 336 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 337 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 338 | CLANG_WARN_STRICT_PROTOTYPES = YES; 339 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 340 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 341 | CLANG_WARN_UNREACHABLE_CODE = YES; 342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 343 | COPY_PHASE_STRIP = NO; 344 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 345 | ENABLE_NS_ASSERTIONS = NO; 346 | ENABLE_STRICT_OBJC_MSGSEND = YES; 347 | GCC_C_LANGUAGE_STANDARD = gnu11; 348 | GCC_NO_COMMON_BLOCKS = YES; 349 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 350 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 351 | GCC_WARN_UNDECLARED_SELECTOR = YES; 352 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 353 | GCC_WARN_UNUSED_FUNCTION = YES; 354 | GCC_WARN_UNUSED_VARIABLE = YES; 355 | IPHONEOS_DEPLOYMENT_TARGET = 14.2; 356 | MTL_ENABLE_DEBUG_INFO = NO; 357 | MTL_FAST_MATH = YES; 358 | SDKROOT = iphoneos; 359 | SWIFT_COMPILATION_MODE = wholemodule; 360 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 361 | VALIDATE_PRODUCT = YES; 362 | }; 363 | name = Release; 364 | }; 365 | 8CA247BB256BAE5B0006B376 /* Debug */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 369 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 370 | CODE_SIGN_STYLE = Automatic; 371 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUIDemos/Preview Content\""; 372 | DEVELOPMENT_TEAM = NWHDL7X5B3; 373 | ENABLE_PREVIEWS = YES; 374 | INFOPLIST_FILE = SwiftUIDemos/Info.plist; 375 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 376 | LD_RUNPATH_SEARCH_PATHS = ( 377 | "$(inherited)", 378 | "@executable_path/Frameworks", 379 | ); 380 | PRODUCT_BUNDLE_IDENTIFIER = com.alexhay.SwiftUIDemos; 381 | PRODUCT_NAME = "$(TARGET_NAME)"; 382 | SWIFT_VERSION = 5.0; 383 | TARGETED_DEVICE_FAMILY = "1,2"; 384 | }; 385 | name = Debug; 386 | }; 387 | 8CA247BC256BAE5B0006B376 /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 391 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 392 | CODE_SIGN_STYLE = Automatic; 393 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUIDemos/Preview Content\""; 394 | DEVELOPMENT_TEAM = NWHDL7X5B3; 395 | ENABLE_PREVIEWS = YES; 396 | INFOPLIST_FILE = SwiftUIDemos/Info.plist; 397 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 398 | LD_RUNPATH_SEARCH_PATHS = ( 399 | "$(inherited)", 400 | "@executable_path/Frameworks", 401 | ); 402 | PRODUCT_BUNDLE_IDENTIFIER = com.alexhay.SwiftUIDemos; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | SWIFT_VERSION = 5.0; 405 | TARGETED_DEVICE_FAMILY = "1,2"; 406 | }; 407 | name = Release; 408 | }; 409 | /* End XCBuildConfiguration section */ 410 | 411 | /* Begin XCConfigurationList section */ 412 | 8CA247A6256BAE580006B376 /* Build configuration list for PBXProject "SwiftUIDemos" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 8CA247B8256BAE5B0006B376 /* Debug */, 416 | 8CA247B9256BAE5B0006B376 /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | 8CA247BA256BAE5B0006B376 /* Build configuration list for PBXNativeTarget "SwiftUIDemos" */ = { 422 | isa = XCConfigurationList; 423 | buildConfigurations = ( 424 | 8CA247BB256BAE5B0006B376 /* Debug */, 425 | 8CA247BC256BAE5B0006B376 /* Release */, 426 | ); 427 | defaultConfigurationIsVisible = 0; 428 | defaultConfigurationName = Release; 429 | }; 430 | /* End XCConfigurationList section */ 431 | }; 432 | rootObject = 8CA247A3256BAE580006B376 /* Project object */; 433 | } 434 | --------------------------------------------------------------------------------