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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:10:01