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

SwiftUI实现长按拖动调整列表项排序功能求助

SwiftUI实现类似Safari标签页的长按拖动排序功能

以下是修改后的完整代码,实现长按列表项拖动调整排序的功能,和Safari标签页的交互逻辑一致:

import SwiftUI

struct HomeView: View {
    
    struct Item: Identifiable {
        var id = UUID()
        var name: String
        var color: Color
    }
    
    @State var items = [
        Item(name: "Item 1", color: .red),
        Item(name: "Item 2", color: .green),
        Item(name: "Item 3", color: .blue)
    ]
    
    // 跟踪当前拖动的元素、初始索引和偏移量
    @State private var draggedItem: Item?
    @State private var initialIndex: Int?
    @State private var dragOffset: CGSize = .zero
    
    var body: some View {
        List {
            ForEach(items.indices, id: \.self) { index in
                let item = items[index]
                Text(item.name)
                    .frame(maxWidth: .infinity)
                    .padding()
                    .background(item.color)
                    .foregroundColor(.white)
                    .cornerRadius(8)
                    // 给拖动中的元素添加偏移和视觉效果
                    .offset(draggedItem?.id == item.id ? dragOffset : .zero)
                    .shadow(color: draggedItem?.id == item.id ? .black.opacity(0.3) : .clear, radius: 8)
                    .zIndex(draggedItem?.id == item.id ? 1 : 0)
                    .onLongPressGesture(minimumDuration: 0.2) {
                        // 长按开始,记录当前元素和初始索引
                        draggedItem = item
                        initialIndex = index
                    }
                    .simultaneousGesture(
                        DragGesture()
                            .onChanged { value in
                                guard draggedItem != nil else { return }
                                // 更新拖动偏移量
                                dragOffset = value.translation
                                
                                // 计算当前拖动位置对应的目标索引
                                let currentY = value.location.y
                                let rowHeight: CGFloat = 60 // 预估行高,可根据实际调整
                                let targetIndex = max(0, min(items.count - 1, Int(currentY / rowHeight)))
                                
                                // 如果目标索引和初始索引不同,交换元素
                                if let initialIdx = initialIndex, initialIdx != targetIndex {
                                    items.swapAt(initialIdx, targetIndex)
                                    initialIndex = targetIndex
                                }
                            }
                            .onEnded { _ in
                                // 拖动结束,重置状态
                                draggedItem = nil
                                initialIndex = nil
                                dragOffset = .zero
                            }
                    )
            }
            .onDelete(perform: deleteItem)
        }
    }
    
    func deleteItem(at offsets: IndexSet) {
        items.remove(atOffsets: offsets)
    }
}

struct HomeView_Previews: PreviewProvider {
    static var previews: some View {
        HomeView()
    }
}

关键实现说明:

  • 状态变量:通过draggedItem跟踪当前拖动的元素,initialIndex记录元素初始位置,dragOffset控制拖动时的视图偏移。
  • 长按触发:用.onLongPressGesture设置短时长触发(0.2秒),触发后记录当前元素和索引。
  • 拖动交互:通过.simultaneousGesture绑定DragGesture,拖动过程中实时更新偏移量,同时根据手指位置计算目标索引,交换数组元素实现排序。
  • 视觉反馈:拖动中的元素会提升层级(zIndex)、添加阴影和偏移,让用户清晰看到拖动状态。

注意:代码中rowHeight是预估的行高度,如果你的列表项高度不同,可以根据实际情况调整,或者通过GeometryReader动态获取行高来提升准确性。

内容的提问来源于stack exchange,提问作者NexusUA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 15:32:43