You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 16中如何兼顾List新初始化器的绑定与搜索功能?

Solution: Combine iOS 16's Editable List with Search Functionality

The core issue here is that iOS 16's new List initializer (with the editActions parameter) expects a binding to your source data collection, but your filteredData is a computed, read-only array that doesn’t connect back to the original data for edits. To fix this, we can create a custom binding that wraps the filtered data and translates edits (delete/move) back to the original array.

Approach 1: Use a Custom Binding (Preserves the New editActions Initializer)

We’ll build a custom Binding<[Int]> that returns your filtered data when read, and updates the original data array when edits are made. This way, we can use the new List initializer while keeping the search functionality intact.

Here’s the modified code:

struct QuestionView: View {

    @State
    private var data: [Int] = Array(1...100)

    @State
    private var searchQuery: String = ""

    var filteredData: [Int] {
        searchQuery.isEmpty ? data : data.filter { $0.description.contains(searchQuery) }
    }

    // Custom binding that maps filtered data edits to the original array
    var filteredDataBinding: Binding<[Int]> {
        Binding(
            get: { filteredData },
            set: { newFilteredItems in
                // Create a map of items to their original positions in the source data
                let originalItemPositions = Dictionary(uniqueKeysWithValues: data.enumerated().map { ($0.element, $0.offset) })
                
                // Get non-filtered items and keep them in their original order
                let nonFilteredItems = data.filter { !newFilteredItems.contains($0) }
                let sortedNonFiltered = nonFilteredItems.sorted {
                    originalItemPositions[$0]! < originalItemPositions[$1]!
                }
                
                // Update the original data: first the reordered filtered items, then non-filtered items in their original order
                data = newFilteredItems + sortedNonFiltered
            }
        )
    }

    var body: some View {
        NavigationStack {
            // Use our custom binding instead of $data
            List(filteredDataBinding, id: \.self, editActions: .all) { $item in
                Text("\(item)")
            }
            .navigationTitle("Example")
            .searchable(text: $searchQuery)
            .toolbar { EditButton() }
        }
    }
}

How This Works:

  • Reading: The binding returns your existing filteredData whenever the search query changes, so the list updates to show only matching items.
  • Deleting: When you delete an item from the filtered list, the binding’s set method receives the filtered array without that item. We then rebuild the original data array to exclude the deleted item, keeping non-filtered items in their original order.
  • Moving: When you reorder items in the filtered list, the set method gets the new sorted filtered array. We combine this with the non-filtered items (sorted by their original positions) to update the source data array correctly.

Approach 2: Manual Edit Handlers (Alternative)

If you prefer to stick with the standard List + ForEach pattern, you can manually implement onDelete and onMove while using your filtered data. This avoids the custom binding but requires writing a bit more code for edit actions:

struct QuestionView: View {

    @State
    private var data: [Int] = Array(1...100)

    @State
    private var searchQuery: String = ""

    var filteredData: [Int] {
        searchQuery.isEmpty ? data : data.filter { $0.description.contains(searchQuery) }
    }

    var body: some View {
        NavigationStack {
            List {
                ForEach(filteredData, id: \.self) { item in
                    Text("\(item)")
                }
                .onDelete { indices in
                    // Convert filtered indices to items and remove them from the original data
                    let itemsToDelete = indices.map { filteredData[$0] }
                    data.removeAll { itemsToDelete.contains($0) }
                }
                .onMove { sourceIndices, destination in
                    // Handle reordering: move filtered items in the original data array
                    let itemsToMove = sourceIndices.map { filteredData[$0] }
                    // Remove items from original data first
                    data.removeAll { itemsToMove.contains($0) }
                    // Find the correct insertion point in the original data
                    let insertionIndex: Int
                    if destination < filteredData.count {
                        let destinationItem = filteredData[destination]
                        insertionIndex = data.firstIndex(of: destinationItem) ?? data.endIndex
                    } else {
                        insertionIndex = data.endIndex
                    }
                    // Insert the reordered items
                    data.insert(contentsOf: itemsToMove, at: insertionIndex)
                }
            }
            .navigationTitle("Example")
            .searchable(text: $searchQuery)
            .toolbar { EditButton() }
        }
    }
}

Both approaches work, but the first one leverages the new iOS 16 editActions initializer as you requested.

内容的提问来源于stack exchange,提问作者Nick Rossik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 10:31:14