如何在列表单元格滑动时禁用工具栏按钮?
实现列表单元格滑动时禁用工具栏按钮
方案一:通过ObservableObject管理滑动状态(纯SwiftUI)
这种方法通过全局状态管理器跟踪单元格的滑动状态,结合滑动操作的回调和交互事件更新状态,进而控制工具栏按钮的启用状态。
- 创建状态管理器
import SwiftUI class ListSlideStateManager: ObservableObject { // 记录当前滑动的单元格索引,nil表示无单元格处于滑动状态 @Published var currentSwipedIndex: Int? // 计算属性:快速判断是否有单元格滑动 var isAnyCellSwiped: Bool { currentSwipedIndex != nil } }
- 父视图集成状态管理器
struct ContentView: View { @StateObject private var slideManager = ListSlideStateManager() // 示例数据 private let items = (1...20).map { Item(id: $0, title: "条目 \($0)") } var body: some View { NavigationStack { List { ForEach(Array(items.enumerated()), id: \.element.id) { index, item in SwipeableCell(item: item, index: index, slideManager: slideManager) } } .toolbar { ToolbarItem(placement: .primaryAction) { Button("工具栏操作") { // 执行按钮逻辑 } // 根据滑动状态禁用按钮 .disabled(slideManager.isAnyCellSwiped) } } // 点击背景取消滑动并重置状态 .background( Color.clear .onTapGesture { slideManager.currentSwipedIndex = nil } ) } } } // 示例数据模型 struct Item: Identifiable { let id: Int let title: String }
- 自定义可滑动单元格
struct SwipeableCell: View { let item: Item let index: Int @ObservedObject var slideManager: ListSlideStateManager var body: some View { Text(item.title) .swipeActions(edge: .trailing) { Button(role: .destructive) { // 执行滑动按钮逻辑(比如删除) slideManager.currentSwipedIndex = nil } label: { Label("删除", systemImage: "trash") } // 滑动按钮出现时,标记当前单元格为滑动状态 .onAppear { slideManager.currentSwipedIndex = index } } // 点击单元格时取消滑动状态 .onTapGesture { slideManager.currentSwipedIndex = nil } } }
方案二:利用UIKit UITableView代理回调(底层原生方式)
如果需要更精准的状态监听,可以直接利用SwiftUI List底层的UITableView代理方法,监听滑动编辑的开始和结束事件:
- 自定义UITableView代理扩展
import SwiftUI import UIKit class SlideDetectionTableViewDelegate: NSObject, UITableViewDelegate { weak var slideManager: ListSlideStateManager? func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) { slideManager?.currentSwipedIndex = indexPath.row } func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) { slideManager?.currentSwipedIndex = nil } } // 包装UITableView以注入自定义代理 struct SlideDetectingList: UIViewRepresentable { @ObservedObject var slideManager: ListSlideStateManager let content: () -> List<Never, Never> func makeUIView(context: Context) -> UITableView { let tableView = UITableView() tableView.delegate = context.coordinator return tableView } func updateUIView(_ uiView: UITableView, context: Context) {} func makeCoordinator() -> SlideDetectionTableViewDelegate { let coordinator = SlideDetectionTableViewDelegate() coordinator.slideManager = slideManager return coordinator } }
- 在父视图中使用自定义List
struct ContentView: View { @StateObject private var slideManager = ListSlideStateManager() private let items = (1...20).map { Item(id: $0, title: "条目 \($0)") } var body: some View { NavigationStack { SlideDetectingList(slideManager: slideManager) { List { ForEach(items) { item in Text(item.title) .swipeActions(edge: .trailing) { Button(role: .destructive) { // 删除逻辑 } label: { Label("删除", systemImage: "trash") } } } } } .toolbar { ToolbarItem(placement: .primaryAction) { Button("工具栏操作") { // 按钮逻辑 } .disabled(slideManager.isAnyCellSwiped) } } } } }
为什么之前的方法无效?
.onAppear/.onDisappear:滑动按钮只是在视图内平移,并未被添加/移除出视图层级,因此这两个回调不会触发。GeometryReader:滑动操作属于UIKit的交互动画,不会触发SwiftUI视图的重绘,因此GeometryReader无法捕获帧变化。
内容的提问来源于stack exchange,提问作者Reinhard Männer
相关产品推荐
相关产品推荐

