在 SwiftUI 搜索栏中应用去抖动

问题描述 投票:0回答:1

我想在搜索栏中应用去抖或延迟,并允许用户在几毫秒内键入文本并根据用户在搜索栏中的搜索文本来过滤数据。我已尝试将下面的代码放入 searchResults Completed 属性中,但实际上不允许我这样做。

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)

实现它的替代方法是什么?

这是我的视图模型代码...

import Foundation

enum ViewState {
  case loading
  case error
  case loaded
  case emptyView
}

protocol PeopleListViewModelAction: ObservableObject {
  func getPeopleList(urlStr: String) async
}

// MARK: - People List ViewModel Implementation.
@MainActor
final class PeopleListViewModel {
  
  @Published private(set) var viewState: ViewState = .loaded
  @Published  var searchText = ""
  private(set)  var filteredPeople: [PeopleData] = []
  private(set) var peopleLists: [PeopleData] = []
  private let repository: PeopleCardsRepository
  
  init(repository: PeopleCardsRepository) {
    self.repository = repository
  }
}

extension PeopleListViewModel: PeopleListViewModelAction {
  
  func getPeopleList(urlStr: String) async {
    viewState = .loaded
    guard let url = URL(string: urlStr) else { return }
    do {
      let lists = try await repository.getPeopleList(for: url)
      peopleLists = lists
      viewState =  peopleLists.isEmpty ? .emptyView : .loaded
    } catch {
      viewState = .error
    }
  }
}

extension PeopleListViewModel {
  
  var searchResults: [PeopleData] {
      guard !searchText.isEmpty else { return peopleLists }
      return peopleLists.filter { $0.firstName.localizedStandardContains(searchText) }
  }
}

这是我的查看代码...

import SwiftUI

struct PeopleListView: View {
  // MARK: - Using State Object to make sure view model object will not destroyed or recreate.
  @StateObject var viewModel: PeopleListViewModel
  @State private var isErrorOccured = true
  
  var body: some View {
    NavigationStack {
      VStack {
        switch viewModel.viewState {
        case .loading:
          ProgressView()
        case .loaded:
          showPeopleListView()
        case .error:
          showErrorView()
        case .emptyView:
          EmptyView()
        }
      }
      .navigationTitle(Text(LocalizedStringKey("People List")))
      .searchable(text: $viewModel.searchText, prompt: "Please enter name..")
    }.task {
      await viewModel.getPeopleList(urlStr: Endpoint.peopleListURL)
    }
  }
  
  @ViewBuilder
  func showPeopleListView() -> some View {
    List(viewModel.searchResults) { peopleList in
      NavigationLink {
        PeopleDetailsView(people: peopleList)
      }label: {
        PeopleCellView(people: peopleList)
      }
    }
  }
  
  @ViewBuilder
  func showErrorView() -> some View {
    ProgressView().alert(isPresented: $isErrorOccured) {
      Alert(title: Text("Error Occured"),message: Text("Something went wrong"),
            dismissButton: .default(Text("Ok")))
    }
  }
}

#Preview {
  PeopleListView(viewModel: PeopleListViewModel(repository: PeopleRepositoryImplementation(networkManager: NetworkManager())))
}

这是错误的屏幕截图..

enter image description here

这是应用程序的屏幕截图:

enter image description here

ios swift swiftui viewmodel debouncing
1个回答
0
投票

.task
旨在取代合并的
@StateObject
,在异步/等待中去抖动它非常简单,只是
Task.sleep
,例如尝试这样的事情:

@State var result: Result<[People], Error>?
..
.task(id: searchText) {
    do {
        try Task.sleep(for: .seconds(0.5)) // debounce
    //either let peopleController = peopleController() or better @Environment(\.peopleController) peopleController
        let people = await peopleController.getPeopleList(searchText: searchText)
        result = .success(people)
    }
    catch {
        result = .failure(error)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.