Home > other >  Having problem making a searchable searchbar in Swift (xcode)
Having problem making a searchable searchbar in Swift (xcode)

Time:12-21

Bin some changes to the Xcode and im a bit stuck on how to make a searchable Search bar when its not a basic list. I have one page with the list with identifiers and another page with the code to embed the list.

Hope it is anyone out there with a bit more nolage then me.

Page 1

import SwiftUI

struct Country: Identifiable {
    
    let id = UUID()
    let imageName: String
    let title: String
    let description: String
    let viewCount: Int
    let uploadDate: String
    let url: URL
}



struct CountryList {
    
        static let AllCountries = [
        Country(imageName: "flag-of-Sweden",
              title: "Sweden",
              description: "lorumibsum lorum ibsum sim sum sam",
              viewCount: 370222,
              uploadDate: "date of post",
              url: URL(string: "https://test.com")!),
        
        Country(imageName: "flag-of-Poland",
              title: "Poland",
              description: "lorumibsum lorum ibsum sim sum sam",
              viewCount: 239150,
              uploadDate: "date of post",
              url: URL(string: "https://test.com")!),
        
        Country(imageName: "flag-of-Russia",
              title: "Russia",
              description: "lorumibsum lorum ibsum sim sum sam",
              viewCount: 162897,
              uploadDate: "date of post",
              url: URL(string: "https://test.com")!),
              
        Country(imageName: "flag-of-Spain",
              title: "Spain",
              description: "lorumibsum lorum ibsum sim sum sam",
              viewCount: 119115,
              uploadDate: "date of post",
              url: URL(string: "https://test.com")!),
        
        Country(imageName: "flag-of-Libya",
              title: "Libya",
              description: "lorumibsum lorum ibsum sim sum sam",
              viewCount: 112213,
              uploadDate: "date of post",
              url: URL(string: "https://test.com")!),

Page 2 This is page 2 where im implementing the list to the app

import SwiftUI



struct CountryListView: View {
    
    var country: [Country] = CountryList.AllCountries
    

    
    @State private var searchText = ""
    
    var body: some View {
        
        NavigationView {
            
            List(country, id: \.id) { country in
                NavigationLink(destination: CountryDetailView(Country: country), label: {
                    CountryCell(Country: country)
                
                    
            })
                

        }
            .navigationTitle("Find your country")
            .searchable(text: $searchText)
            
        }
    }
        

struct CountryCell: View {
    var Country: Country
   
    
    var body: some View {
        HStack {
            Image(Country.imageName)
                .resizable()
                .scaledToFit()
                .frame(height: 70)
                .cornerRadius(16)
                .padding(.vertical, 4)
                
        
            
            VStack(alignment: .leading, spacing: 5) {
                Text(Country.title)
                    .fontWeight(.semibold)
                    .lineLimit(2)
                    .minimumScaleFactor(0.5)
                
                Text(Country.uploadDate)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }
        }
        }
    }
    var searchResults: [String] {
        if searchText.isEmpty {
            return Country
        } else {
            return Country.filter({ $0.contains(searchText)})
        }
    }

}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        CountryListView()
       
    }
}

CodePudding user response:

There are a number of things not correct in your code. Try/study this code to achieve what you want.

import SwiftUI


@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    var body: some View {
        CountryListView()
    }
}

struct Country: Identifiable {
    let id = UUID()
    let imageName: String
    let title: String
    let description: String
    let viewCount: Int
    let uploadDate: String
    let url: URL
}

struct CountryList {
    
    static let AllCountries = [
        Country(imageName: "flag-of-Sweden",
                title: "Sweden",
                description: "lorumibsum lorum ibsum sim sum sam",
                viewCount: 370222,
                uploadDate: "date of post",
                url: URL(string: "https://test.com")!),
        
        Country(imageName: "flag-of-Poland",
                title: "Poland",
                description: "lorumibsum lorum ibsum sim sum sam",
                viewCount: 239150,
                uploadDate: "date of post",
                url: URL(string: "https://test.com")!),
        
        Country(imageName: "flag-of-Russia",
                title: "Russia",
                description: "lorumibsum lorum ibsum sim sum sam",
                viewCount: 162897,
                uploadDate: "date of post",
                url: URL(string: "https://test.com")!),
        
        Country(imageName: "flag-of-Spain",
                title: "Spain",
                description: "lorumibsum lorum ibsum sim sum sam",
                viewCount: 119115,
                uploadDate: "date of post",
                url: URL(string: "https://test.com")!),
        
        Country(imageName: "flag-of-Libya",
                title: "Libya",
                description: "lorumibsum lorum ibsum sim sum sam",
                viewCount: 112213,
                uploadDate: "date of post",
                url: URL(string: "https://test.com")!),
        
    ]
    
}

struct CountryListView: View {
    @State var countries: [Country] = CountryList.AllCountries
    
    @State private var searchText = ""
    
    var body: some View {
        NavigationView {
            List(searchResults) { country in
                NavigationLink(destination: CountryDetailView(Country: country), label: {
                    CountryCell(country: country)
                })
            }
            .navigationTitle("Find your country")
            .searchable(text: $searchText)
        }.navigationViewStyle(.stack)
    }
    
    // adjust according to your needs
    var searchResults: [Country] {
        let textSearch = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        if searchText.isEmpty {
            return countries
        } else {
            return countries.filter{ $0.title.trimmingCharacters(in: .whitespacesAndNewlines).lowercased().starts(with: textSearch) }
        }
    }
    
}

struct CountryCell: View {
    var country: Country
    
    var body: some View {
        HStack {
            Image(country.imageName)
                .resizable()
                .scaledToFit()
                .frame(height: 70)
                .cornerRadius(16)
                .padding(.vertical, 4)
            
            VStack(alignment: .leading, spacing: 5) {
                Text(country.title)
                    .fontWeight(.semibold)
                    .lineLimit(2)
                    .minimumScaleFactor(0.5)
                
                Text(country.uploadDate)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }
        }
    }
}

CodePudding user response:

Welcome to Stack Overflow! Please take the tour and see: How do I ask a good question? and How to create a Minimal, Reproducible Example.

The search bar and its pattern do not change whenever you are using a list. Where you got confused is not understanding that you are returning an array of the type of your data, not just a string. [String] are often used as examples because they are simple, but the confusion comes from the fact that your search text is also a String. Really, you are returning an [Type] where Type is the type of your data. In this case, that is a Country. Therefore your code becomes this:

struct CountryListView: View {
    
    // You should just declare this as @State var, NEVER just var in a struct.
    // If you are not planning to mutate this list, declare it with let
    // Also, you reused "Country" which caused the compiler to be confused.
    // Types should begin with a capital letter and be singular
    // Names of singular instances should be lowercased.
    // Names of arrays of a type should be lowercased and plural
    // You will see I changed the instance you called Country to countries.
    @State private var countries = CountryList.allCountries
    @State private var searchText = ""
    
    var body: some View {
        
        NavigationView {
            // The list uses searchResults which is an [Country]
            // Since Country is Identifiable, you do not need the id:\.id in the list.
            List(searchResults) { country in
                // You didn't provide CountryDetail, so I used CountryCell for both
                NavigationLink(destination: CountryCell(country: country), label: {
                    CountryCell(country: country)
                })
            }
            .navigationTitle("Find your country")
            .searchable(text: $searchText)
        }
    }
    
    var searchResults: [Country] {
        if searchText.isEmpty {
            return countries
        } else {
            return countries.filter({ $0.title.lowercased().contains(searchText.lowercased())})
        }
    }

    struct CountryCell: View {
        
        let country: Country
        
        var body: some View {
            HStack {
                // Since we don't have the flag assets, I just used an SF Symbol
                // Please do this sort of substitution before you post.
                Image(systemName: country.imageName)
                    .resizable()
                    .scaledToFit()
                    .frame(height: 70)
                    .cornerRadius(16)
                    .padding(.vertical, 4)
                
                VStack(alignment: .leading, spacing: 5) {
                    Text(country.title)
                        .fontWeight(.semibold)
                        .lineLimit(2)
                        .minimumScaleFactor(0.5)
                    
                    Text(country.uploadDate)
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                }
            }
        }
    }
}

struct CountryList {
    
    static let allCountries = [
        // Substitued SF Font for imageName
        Country(imageName: "flag",
                title: "Sweden",
                description: "lorumibsum lorum ibsum sim sum sam",
                viewCount: 370222,
                uploadDate: "date of post",
                url: URL(string: "https://test.com")!),
        ...]
}

A few additional thoughts:

Next time before you post your code, simplify it in another project to be as minimal as possible, and still show your issues. There were a lot of things missing in this code, as well as a lot of additional lines that were irrelevant.

Also, be careful with your naming conventions as outlined int he comments.

  • Related