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

NavigationSplitView搭配.searchable modifier崩溃问题咨询:原因与解法

struct NavStackListSearchView: View {
    @State private var items = ["Item1", "Item2", "Item3", "Item4", "Item5"]
    @State private var selectedItems: Set<String> = []
    @State private var searchText: String = ""
    
    var filteredItems: [String] {
        if searchText.isEmpty {
            return items
        } else {
            return items.filter { $0.contains(searchText) }
        }
    }
        
    var body: some View {
        NavigationStack {
            List(selection: $selectedItems) {
                ForEach(filteredItems, id: \.self) { item in
                    NavigationLink(item) {
                        Text(item).font(.title3)
                    }
                }
            }
        }
        .searchable(text: $searchText)
        .onChange(of: searchText) { _ in
            selectedItems = []
        }
    }
}
struct NavSplitListSearchView: View {
    @State private var items = ["Item1", "Item2", "Item3", "Item4", "Item5"]
    @State private var selectedItems: Set<String> = []
    @State private var searchText: String = ""
    
    var filteredItems: [String] {
        if searchText.isEmpty {
            return items
        } else {
            return items.filter { $0.contains(searchText) }
        }
    }
        
    var body: some View {
        NavigationSplitView {
            List(selection: $selectedItems) {
                ForEach(filteredItems, id: \.self) { item in
                    Text(item)
                }
            }
        } detail: {
            if selectedItems.isEmpty {
                Text("Pick an item")
            } else {
                Text("\(selectedItems.first ?? "--no item--")")
            }
        }
        .searchable(text: $searchText)
        .onChange(of: searchText) { _ in
            selectedItems = []
        }
    }
}

崩溃复现步骤

  • 启动应用
  • 点击"Item3"
  • 返回(若处于紧凑模式)
  • 搜索"3"
  • 点击显示的唯一条目"Item3"
    → 发生崩溃

开发环境:iOS 16.2、Xcode 14.2


崩溃原因

核心问题是NavigationSplitView的List选择机制与动态过滤的数据源存在状态冲突:

  1. 当你先选中Item3,搜索过滤后仅保留Item3的列表时,系统尝试更新选中状态,但由于你用字符串本身作为id,List内部会将原始列表中的Item3与过滤后列表中的Item3判定为不同实例,触发内部状态不一致导致崩溃。
  2. 你遗漏了NavigationSplitView对数据源稳定性的要求:直接用过滤后的动态数组作为ForEach数据源,会导致List的视图结构频繁变更,而选中状态的绑定没有与数据源更新完全同步,进而引发断言失败。

解决方法

方法一:稳定数据源+条件显示

用原始数组作为ForEach的数据源,通过hidden和opacity控制条目是否显示,保持List内部标识的稳定性:

struct NavSplitListSearchView: View {
    @State private var items = ["Item1", "Item2", "Item3", "Item4", "Item5"]
    @State private var selectedItems: Set<String> = []
    @State private var searchText: String = ""
        
    var body: some View {
        NavigationSplitView {
            List(selection: $selectedItems) {
                ForEach(items, id: \.self) { item in
                    Text(item)
                        .hidden(!item.contains(searchText))
                        .opacity(item.contains(searchText) ? 1 : 0) // 维持布局高度一致
                }
            }
        } detail: {
            if selectedItems.isEmpty {
                Text("Pick an item")
            } else {
                Text("\(selectedItems.first ?? "--no item--")")
            }
        }
        .searchable(text: $searchText)
        .onChange(of: searchText) { _ in
            selectedItems = []
        }
    }
}

方法二:使用带唯一ID的数据模型

定义带唯一UUID的模型,避免用内容作为标识,确保过滤前后元素的ID稳定:

struct Item: Identifiable, Hashable {
    let id = UUID()
    let name: String
}

struct NavSplitListSearchView: View {
    @State private var items = [
        Item(name: "Item1"),
        Item(name: "Item2"),
        Item(name: "Item3"),
        Item(name: "Item4"),
        Item(name: "Item5")
    ]
    @State private var selectedItems: Set<Item> = []
    @State private var searchText: String = ""
    
    var filteredItems: [Item] {
        if searchText.isEmpty {
            return items
        } else {
            return items.filter { $0.name.contains(searchText) }
        }
    }
        
    var body: some View {
        NavigationSplitView {
            List(selection: $selectedItems) {
                ForEach(filteredItems) { item in
                    Text(item.name)
                }
            }
        } detail: {
            if selectedItems.isEmpty {
                Text("Pick an item")
            } else {
                Text("\(selectedItems.first?.name ?? "--no item--")")
            }
        }
        .searchable(text: $searchText)
        .onChange(of: searchText) { _ in
            selectedItems = []
        }
    }
}

方法二更符合SwiftUI最佳实践,尤其适合存在重复内容的数据集。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:35:15