SwiftUI List中ContextMenu的.disabled修饰符生效异常问题咨询
问题答复
- 不需要通过
DispatchQueue.main.async修改redButton状态,你的问题和线程调度无关,该方法无法修复当前异常。
异常成因
- 状态作用域错误:你定义的
@State private var redButton是全局属于Test视图的状态,所有ForEach循环生成的列表项共用这一个状态变量,没有和具体的列表项绑定,所有项的ContextMenu禁用逻辑都受同一个值控制。 - List复用机制影响:SwiftUI的List底层和UIKit的UITableView一致,自带视图复用逻辑,当你修改全局状态触发视图刷新时,只会更新当前可见/正在被复用的Cell,就会出现禁用状态随机生效、仅部分Cell生效的问题。
移除List后没有复用逻辑,且仅单个按钮使用该状态,所以不会出现异常。
修复方案
有两种常用的修复方案:
方案1:用集合存储已禁用的项ID
适合需要在父视图统一管理所有列表项状态的场景,将状态和列表项的唯一ID绑定:
struct Test: View { // 存储所有已停用的列表项ID @State private var deactivatedIds: Set<Int> = [] var body: some View { List { ForEach(1...10, id: \.self) { number in Button { // 点击按钮恢复当前项的可用状态 deactivatedIds.remove(number) } label: { Text("Button \(number)") }.contextMenu { Button { // 停用当前选中的项 deactivatedIds.insert(number) } label: { Text("Deactivate") } // 仅当前项ID在禁用集合中时才禁用 .disabled(deactivatedIds.contains(number)) } } } } }
方案2:抽取独立子视图,状态内部管理
适合列表项状态独立、不需要父视图统一管控的场景,每个子视图自己持有状态,不受复用影响:
struct Test: View { var body: some View { List { ForEach(1...10, id: \.self) { number in ListItemView(number: number) } } } } // 独立的列表项子视图,自己管理停用状态 struct ListItemView: View { let number: Int @State private var isDeactivated = false var body: some View { Button { isDeactivated = false } label: { Text("Button \(number)") }.contextMenu { Button { isDeactivated = true } label: { Text("Deactivate") } .disabled(isDeactivated) } } }
内容的提问来源于stack exchange,提问作者AlexS
相关产品推荐
相关产品推荐

