搜索后SwiftUI无限列表刷新

时间:2021-06-29 12:30:24

标签: swiftui

我正在 SwiftUI 中创建一个可搜索的无限滚动列表。当视图用于显示数据时,事情似乎正常工作。但是,除了显示数据之外,我还想添加搜索功能。这是我最初的看法,但不太奏效:

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()
    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.items) { item in
                    Text(item.text)
                        .onAppear() { viewModel.loadMoreContentIfNeeded(currentItem: item) }
                }
        }
        .searchable("Search", text: $viewModel.searchString)
        .onAppear { viewModel.onAppear() }
   }
}

class ViewModel: ObservableObject {
    @Published var items: [ListItem] = []
    @Published var searchString: String = ""

    private let batchSize = 2
    private var debouncedSearchString: String = ""
    private var cancellables = Set<AnyCancellable>()

    init() {
        $searchString
            .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
            .removeDuplicates()
            .dropFirst()
            .sink { self.performSearch(for: $0) }
            .store(in: &cancellables)
    }

    deinit {
        cancellables.removeAll()
    }

    func onAppear() {
        loadMoreContent()
    }

    func loadMoreContentIfNeeded(currentItem item: ListItem) {
        guard item == items.last else {
            return
        }
        loadMoreContent()
    }

    private func loadMoreContent() {
        loadContent(offset: items.count, limit: batchSize, replace: false)
    }

    private func loadContent(offset: Int, limit: Int, replace: Bool) {
        if debouncedSearchString.isEmpty {
            let startIndex = min(offset, masterItemList.count)
            let endIndex = min(startIndex + limit, masterItemList.count)
            let items = masterItemList[startIndex ..< endIndex]
            if replace {
                self.items = Array(items)
            } else {
                self.items += items
            }
        } else {
            let matches = masterItemList.filter { $0.text.lowercased().contains(debouncedSearchString) }
            let startIndex = min(offset, matches.count)
            let endIndex = min(startIndex + limit, matches.count)
            let items = matches[startIndex ..< endIndex]
            if replace {
                self.items = Array(items)
            } else {
                self.items += items
            }
        }
    }

    private func performSearch(for searchString: String) {
        debouncedSearchString = searchString.lowercased()
        loadContent(offset: 0, limit: batchSize, replace: true)
    }
}

struct ListItem: Identifiable, Hashable, Equatable {
    let id = UUID()
    let text: String
}

let stateNames = ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]

let masterItemList: [ListItem] = stateNames.map { ListItem(text: $0) }

我面临的挑战是如何在搜索结果的初始批次已经显示后加载更多行。发生这种情况时,不会为视图调用 onAppear,因为它们已经显示,并且由于没有调用 onAppear,因此不会加载其他内容。

例如,搜索 A 会显示最初的两个结果(阿拉巴马州和阿拉斯加州),但不会显示后续搜索结果。

在这种情况下,如何触发显示其他搜索结果?

0 个答案:

没有答案
相关问题