Search code examples
searchswiftuiios16

How to select an item from a search file and place in textfield in another file


Using SwiftUI - Xcode 14.2 - iOS 16.0

I have tried different search tutorials to create a search file for my project but am unable to find out how to select the item in the search file and place that selected item in a textfield in another file. I have searched this site for other posts, i tried searching through Google, YouTube, etc...

In File 1, I have a textfield that that has a prompt 'start typing' and when selected, it directs you to the Search file to select the item you want, so it can be placed in place of the prompt.

File 1 (where the textfield is needed to paste the selected item):

VStack {
     NavigationLink(destination: NameSearch()) {
         TextField("Name", text: .constant(""), prompt: Text("   Start typing  ")
              .foregroundColor(.blue))
              .multilineTextAlignment(.leading)
              .padding()
     }
}

Once I click on the 'start typing' prompt, it navigates to NameSearch.swift file, as seen below.

NameSearch.swift:

import SwiftUI

struct NameSearch: View {
    
    let name = [
        "Jane", "George", "Sam", "Henry", "Sally", "Liz", "John"
    ]
    
    @State private var searchText = ""
    
    var body: some View {
        
        NavigationStack {
            VStack {
                // Search view
                SearchBarView(searchText: $searchText)
                
                List {
                    // Filtered list of names
                    ForEach(name.filter{$0.hasPrefix(searchText) || searchText == ""}, id:\.self) {
                        searchText in Text(searchText)
                    }
                }
                .navigationBarTitle(Text("Search Name"))
                .resignKeyboardOnDragGesture()
            }
        }
    }
}


struct NameSearch_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            NameSearch()
                .environment(\.colorScheme, .light)
            NameSearch()
                .environment(\.colorScheme, .dark)
        }
    }
}

extension UIApplication {
    func endEditing(_ force: Bool) {
        self.windows
            .filter{$0.isKeyWindow}
            .first?
            .endEditing(force)
    }
}

struct ResignKeyboardOnDragGesture: ViewModifier {
    var gesture = DragGesture().onChanged{_ in
        UIApplication.shared.endEditing(true)
    }
    func body(content: Content) -> some View {
        content.gesture(gesture)
    }
}

extension View {
    func resignKeyboardOnDragGesture() -> some View {
        modifier(ResignKeyboardOnDragGesture())
    }
}


struct SearchBarView: View {
    
    @Binding var searchText: String
    @State private var showCancelButton: Bool = false
    var onCommit: () ->Void = {print("onCommit")}
    
    var body: some View {
        HStack {
            HStack {
                Image(systemName: "magnifyingglass")
                
                // Search text field
                ZStack (alignment: .leading) {
                    if searchText.isEmpty { // Separate text for placeholder to give it the proper color
                        Text("Search")
                    }
                    TextField("", text: $searchText, onEditingChanged: { isEditing in
                        self.showCancelButton = true
                    }, onCommit: onCommit).foregroundColor(.primary)
                }
                // Clear button
                Button(action: {
                    self.searchText = ""
                }) {
                    Image(systemName: "xmark.circle.fill").opacity(searchText == "" ? 0 : 1)
                }
            }
            .padding(EdgeInsets(top: 8, leading: 6, bottom: 8, trailing: 6))
            .foregroundColor(.secondary) // For magnifying glass and placeholder test
            .background(Color(.tertiarySystemFill))
            .cornerRadius(10.0)
            
            if showCancelButton  {
                // Cancel button
                Button("Cancel") {
                    UIApplication.shared.endEditing(true) // this must be placed before the other commands here
                    self.searchText = ""
                    self.showCancelButton = false
                }
                .foregroundColor(Color(.systemBlue))
            }
        }
        .padding(.horizontal)
        .navigationBarHidden(showCancelButton)
    }
}

Question 1: How do I hide all the names from showing in the list so that I just see the search bar and the cancel button and an empty list?

Question 2: Once I type the name I am looking for, it should pop up and I want to select name - how can I do this?

  • once I type the name in search bar, it appears in the empty list
  • I select that name
  • it then takes me back to File 1
  • replaces the 'start typing' prompt with the name i just selected in the Search file.

Question 3: I have noticed in the Search file, I am getting a warning with the following code. How can I resolve it?

extension UIApplication {
    func endEditing(_ force: Bool) {
        self.windows
            .filter{$0.isKeyWindow}
            .first?
            .endEditing(force)
    }
}

The warning that appears is:

'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead


Solution

  • Firstly, thank you for providing a working example of your code.

    As you're building for iOS 15+, you should probably be using the .searchable modifier rather than rolling your own.

    The 2021 WWDC video introducing this feature is here https://developer.apple.com/wwdc21/10176

    Some new features from 2022 here: https://developer.apple.com/wwdc22/10052