SwiftUI拖动列表行时修改未选中行背景色的实现方法
解决方案
可以实现这个需求,核心思路是通过监听UITableView的拖动事件(SwiftUI List底层基于UITableView)来检测拖动的开始和结束,再结合状态变量动态控制列表项的背景色。具体实现步骤如下:
1. 优化数据模型
给TestItem添加Identifiable协议,方便跟踪和识别每个列表项:
struct TestItem: Equatable, Hashable, Identifiable { let id = UUID() var title: String var logo: String? = nil }
2. 创建UITableView拖动状态观察者
通过UIViewRepresentable桥接UIKit,监听UITableView的拖动开始与结束事件:
struct TableDragObserver: UIViewRepresentable { var onDragStart: (UUID?) -> Void var onDragEnd: () -> Void func makeUIView(context: Context) -> UIView { let view = UIView() // 延迟获取底层UITableView实例 DispatchQueue.main.async { guard let tableView = view.superview?.superview as? UITableView else { return } tableView.dragDelegate = context.coordinator tableView.dropDelegate = context.coordinator } return view } func updateUIView(_ uiView: UIView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITableViewDragDelegate, UITableViewDropDelegate { let parent: TableDragObserver init(_ parent: TableDragObserver) { self.parent = parent } // 触发拖动开始回调 func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { if let itemIdString = tableView.cellForRow(at: indexPath)?.accessibilityIdentifier, let itemId = UUID(uuidString: itemIdString) { parent.onDragStart(itemId) } return [] } // 触发拖动结束回调 func tableView(_ tableView: UITableView, dragSessionDidEnd session: UIDragSession) { parent.onDragEnd() } } }
3. 改造主视图控制背景色
在TestView中添加状态变量跟踪拖动状态,嵌入观察者并动态设置列表项背景色:
@available(iOS 15.0, *) struct TestView: View { @ObservedObject var viewModel: ViewModel @State private var isDragging = false @State private var draggedItemId: UUID? init() { viewModel = ViewModel() } var body: some View { VStack(spacing: 0) { Spacer().frame(height: 24) List { ForEach(viewModel.datasource) { item in TestViewItem(title: item.title) .accessibilityIdentifier(item.id.uuidString) // 标记列表项唯一ID .listRowBackground( // 根据拖动状态切换背景色 isDragging && item.id != draggedItemId ? Color.blue : Color.green ) } .onMove { from, to in viewModel.move(from: from, to: to) } } .frame(maxWidth: .infinity, maxHeight: .infinity) .listRowSeparator(.hidden) .environment(\.editMode, .constant(.active)) .listStyle(.plain) // 添加拖动观察者,禁止点击避免干扰交互 .overlay( TableDragObserver( onDragStart: { id in isDragging = true draggedItemId = id }, onDragEnd: { isDragging = false draggedItemId = nil } ).allowsHitTesting(false) ) } .background(Color.gray.ignoresSafeArea()) .frame(maxWidth: .infinity, maxHeight: .infinity) } }
效果说明
- 拖动开始时,
isDragging切换为true,记录当前拖动项的ID,此时未被拖动的列表项背景色改为自定义颜色(示例中为蓝色),被拖动项保持原有绿色。 - 拖动结束时,状态重置,所有列表项恢复初始背景色。
- 通过
accessibilityIdentifier关联列表项与数据模型ID,确保准确识别被拖动的目标项。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

