Home > Net >  Filter function inside For Each SwiftUI
Filter function inside For Each SwiftUI

Time:09-09

I am trying to filter through data that I have retrieved from an API based on a model of car, I have a @State that holds an empty string initially and will update based on what the user has typed. I can't figure out how to filter the data based on the input inside of my existing ForEach loop, any help would be appreciated

import SwiftUI

struct HomeView: View {
  @EnvironmentObject var users: Network
  let columns = [
    GridItem(.adaptive(minimum: 150))
  ]
  @State var isActive: Bool = false
  @State private var shouldAnimate = false
  @State var show = false
  @State private var searchText = ""
  @Environment(\.colorScheme) var colorScheme
    var searchResults: [Car] {
        let searchModel = String(searchText)
        return searchModel == nil ? users.users : users.users.filter{$0.model == searchModel}
    }

    
  var body: some View {
    ScrollView {

      if self.users.users.count > 0 {
        Text("All \(self.users.users[1].make) vehicles")
          .bold()
          .textCase(.uppercase)
          .font(.headline)
      } else {
        Spacer()
        Spacer()
        HStack(alignment: VerticalAlignment.center) {
          Circle()
            .fill(shouldAnimate ? Color.orange : Color.white)
            .frame(width: 20, height: 20)
            .scaleEffect(shouldAnimate ? 1.0 : 0.5)
            .animation(
              Animation.easeInOut(duration: 0.5).repeatForever(autoreverses: true), value: show)
          Circle()
            .fill(shouldAnimate ? Color.orange : Color.white)
            .frame(width: 20, height: 20)
            .scaleEffect(shouldAnimate ? 1.0 : 0.5)
            .animation(
              Animation.easeInOut(duration: 0.5).repeatForever(autoreverses: true).delay(0.3),
              value: show)

          Circle()
            .fill(shouldAnimate ? Color.orange : Color.white)
            .frame(width: 20, height: 20)
            .scaleEffect(shouldAnimate ? 1.0 : 0.5)
            .animation(
              Animation.easeInOut(duration: 0.5).repeatForever(autoreverses: true).delay(0.6),
              value: show)
        }
        Spacer()
        VStack {
          Text("Retrieving the Data...")
            .foregroundColor(Color.white)
            .textCase(.uppercase)
        }

      }
      SearchBar(text: $searchText)
        .padding(.top, -30)
      LazyVGrid(columns: columns, spacing: 10) {
        ForEach(searchResults) { car in
          if car.year >= 2017 {
            HStack(alignment: .top) {
                
              VStack {
                Image(systemName: "car.fill")
                  .resizable()
                  .scaledToFit()
                  .frame(width: 140.0, height: 70)
                  .foregroundColor(.white)

                Text(car.model ?? "lol")
                  .font(.system(size: 15, weight: .bold, design: .default))
                  .foregroundColor(colorScheme == .dark ? Color.white : Color.black)
                  .frame(maxWidth: .infinity, alignment: .center)

                Text(String(car.year))
                  .font(.system(size: 16, weight: .regular, design: .default))
                  .foregroundColor(.orange)
                  .frame(maxWidth: .infinity, alignment: .center)

                Text(car.make)
                  .font(.system(size: 16, weight: .bold, design: .default))
                  .foregroundColor(colorScheme == .dark ? Color.white : Color.gray)
                  .frame(maxWidth: .infinity, alignment: .center)

              }

              .frame(height: 250)
              .background(colorScheme == .dark ? Color.black.opacity(0.2) : Color.white)
              .cornerRadius(20)
              .padding()
              .clipped()
              .shadow(color: Color.black.opacity(0.24), radius: 10, x: 0, y: 0)

            }
          }
        }
      }

    }
    .background(colorScheme == .dark ? Color.black.opacity(0.1) : Color.white)

    .onAppear {
      users.getCars()
      self.shouldAnimate = true
      self.show = true
    }
  }
}
struct HomeView_Previews: PreviewProvider {
  static var previews: some View {
    HomeView()
      .environmentObject(Network())
  }
}

Data:

[
    {
        "id": 1119,
        "make": "Audi",
        "model": "100",
        "generation": "100 Avant (4A,C4)",
        "engine_modification": "2.8 V6 E (174 Hp) quattro Automatic",
        "year": 1991,
        "powertrain_architecture": "Internal Combustion engine",
        "body_type": "Station wagon (estate)",
        "number_of_seats": 5,
        "number_of_doors": 5,
        "urban_fuel_consumption": 13.5,
        "extra_urban_fuel_consumption": 8,
        "combined_fuel_consumption": 10,
        "fuel_type": "Petrol (Gasoline)",
        "acceleration": 9.5,
        "top_speed": 207,
        "power": 174,
        "torque": 250,
        "engine_location": "Front, Longitudinal",
        "engine_displacement": 2771,
        "number_of_cylinders": 6,
        "position_of_cylinders": "V-engine",
        "number_of_valves_per_cylinder": 2,
        "fuel_system": "Multi-point indirect injection",
        "engine_aspiration": "Naturally aspirated engine",
        "kerb_weight": 1550,
        "fuel_tank_capacity": 80,
        "drive_wheel": "All wheel drive (4x4)",
        "number_of_gears": 4,
        "front_brakes": "Ventilated discs",
        "rear_brakes": "Disc"
    },
    {
        "id": 1120,
        "make": "Audi",
        "model": "100",
        "generation": "100 Avant (4A,C4)",
        "engine_modification": "2.8 V6 E (174 Hp) quattro",
        "year": 1991,
        "powertrain_architecture": "Internal Combustion engine",
        "body_type": "Station wagon (estate)",
        "number_of_seats": 5,
        "number_of_doors": 5,
        "urban_fuel_consumption": 13.5,
        "extra_urban_fuel_consumption": 8,
        "combined_fuel_consumption": 10,
        "fuel_type": "Petrol (Gasoline)",
        "acceleration": null,
        "top_speed": null,
        "power": 174,
        "torque": 250,
        "engine_location": "Front, Longitudinal",
        "engine_displacement": 2771,
        "number_of_cylinders": 6,
        "position_of_cylinders": "V-engine",
        "number_of_valves_per_cylinder": 2,
        "fuel_system": "Multi-point indirect injection",
        "engine_aspiration": "Naturally aspirated engine",
        "kerb_weight": 1550,
        "fuel_tank_capacity": 80,
        "drive_wheel": "All wheel drive (4x4)",
        "number_of_gears": 5,
        "front_brakes": "Ventilated discs",
        "rear_brakes": "Disc"
    },

users.users


import Foundation

struct Car: Hashable,Codable, Identifiable {
    let id : Int?
    var make: String
    var model: String?
    var generation: String?
    var engine_modification: String?
    var year: Int 
    var powertrain_architecture: String?
    var body_type: String?
    var number_of_seats: Int?
    var number_of_doors: Int?
    var urban_fuel_consumption: Double?
    var extra_urban_fuel_consumption: Double?
    var combined_fuel_consumption: Double?
    var fuel_type: String?
    var acceleration: Double? // was Int
    var top_speed: Int? // was Int
    var power: Int?
    var torque: Int?
    var engine_location: String?
    var engine_displacement: Int?
    var number_of_cylinders: Int?
    var position_of_cylinders: String?
    var number_of_valves_per_cylinder: Int?
    var fuel_system: String?
    var engine_aspiration: String?
    var kerb_weight: Int?
    var fuel_tank_capacity: Double?
    var drive_wheel: String?
    var number_of_gears: Int?
    var front_brakes: String?
    var rear_brakes: String?
}

CodePudding user response:

try something like this example code, assuming you want to search by year:

@State var searchText = ""

var searchResults: [Car] {
    let searchYear = Int(searchText)
    searchYear == nil ? users.users : users.users.filter{$0.year == searchYear!}
}

var body: some View {
    // ...
    SearchBar(text: $searchText)
    // ...
    LazyVGrid(columns: columns, spacing: 10) {
        ForEach(searchResults) { car in
            HStack(alignment: .top) {
                VStack {
                    // ...
                }
            }
        }
    }
}

EDIT-1: to search for car model, try this:

var searchResults: [Car] {
    searchText.isEmpty ? users.users : users.users.filter{
        $0.model == nil ? false : $0.model!.contains(searchText)
    }
}

EDIT-2: here is the code I used in my tests, to show that the approach works:

struct Car: Identifiable {
    let id = UUID()
    var year: Int
    var make: String
    var model: String?
}

struct ContentView: View {
    @State var cars = [Car(year: 123, make: "aaa", model: "bbb"),
                       Car(year: 456, make: "cc", model: "dd"),
                       Car(year: 789, make: "eee", model: "dd")]
    
    @State private var searchText = ""
    
    var searchResults: [Car] {
        searchText.isEmpty ? cars : cars.filter{
            $0.model == nil ? false : $0.model!.contains(searchText)
        }
    }
    
    var body: some View {
        NavigationView {
            List (searchResults) { car in
                Text(car.model ?? "no data")
            }
        }
        .searchable(text: $searchText)
    }
}
  • Related