NavigationSplitView侧边栏列表中NavigationLink选中状态异常问题
问题原因
这是macOS 14及以后版本中SwiftUI List视图复用机制导致的UI状态不同步问题:当选中行滚出视野后,对应的List单元格会被系统复用给新行,而原选中状态的UI标记没有被正确重置,导致返回滚动时旧行仍显示选中样式,尽管selection绑定的实际值是正确的。
解决方案
方案1:自定义行选中背景,强制UI与状态同步
通过手动设置行的背景样式,覆盖系统默认的选中样式,确保UI完全跟随selection状态变化:
struct ContentView: View { @State private var selection: Int? = nil var body: some View { NavigationSplitView { List(selection: $selection) { ForEach(0...60, id: \.self) { item in NavigationLink("Row \(item)", value: item) // 自定义选中背景,匹配系统原生样式 .listRowBackground(selection == item ? Color.accentColor.opacity(0.2) : Color.clear) } } } content: { Text("Selected row is \(selection ?? 0)") } detail: { Text("Selected row is \(selection ?? 0)") } } }
方案2:给行添加动态ID,强制视图刷新
通过为每个行设置包含selection状态的动态ID,让SwiftUI在选中状态变化时重新渲染行视图,避免复用导致的状态残留:
struct ContentView: View { @State private var selection: Int? = nil var body: some View { NavigationSplitView { List(selection: $selection) { ForEach(0...60, id: \.self) { item in NavigationLink("Row \(item)", value: item) // 动态ID包含选中状态,确保状态变化时视图刷新 .id("\(item)-\(selection ?? -1)") } } } content: { Text("Selected row is \(selection ?? 0)") } detail: { Text("Selected row is \(selection ?? 0)") } } }
方案3:使用带tag和selection参数的NavigationLink
改用显式绑定tag和selection的NavigationLink初始化方式,强化状态与UI的绑定关系:
struct ContentView: View { @State private var selection: Int? = nil var body: some View { NavigationSplitView { List { ForEach(0...60, id: \.self) { item in NavigationLink( "Row \(item)", tag: item, selection: $selection ) { Text("Selected row is \(item)") } } } } content: { Text("Selected row is \(selection ?? 0)") } detail: { Text("Selected row is \(selection ?? 0)") } } }
以上三种方案都能解决该UI异常问题,其中方案1最贴近系统原生样式,方案2和3则从视图渲染机制层面避免了状态残留。
内容的提问来源于stack exchange,提问作者horace1921
相关产品推荐
相关产品推荐

