iOS 16中如何兼顾List新初始化器的绑定与搜索功能?
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
filteredDatawhenever 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
setmethod receives the filtered array without that item. We then rebuild the originaldataarray to exclude the deleted item, keeping non-filtered items in their original order. - Moving: When you reorder items in the filtered list, the
setmethod gets the new sorted filtered array. We combine this with the non-filtered items (sorted by their original positions) to update the sourcedataarray 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

