滑动删除功能

问题描述 投票:0回答:1

使用下面的代码,如何在任务上从右向左滑动才能选择删除它。

我尝试过使用

.SwipeAction
,但没有成功,并且不知道该使用什么

这是我的观点,我正在使用核心数据

import CoreData
import SwiftUI

struct TaskView: View {
    
    @FetchRequest(sortDescriptors: []) var tasks: FetchedResults<Tasks>
    
    @State private var isPresented = false
    
    @EnvironmentObject var taskModel: TaskViewModel
    @Environment(\.managedObjectContext) var moc
    
    @State var TaskStatus: [String] = ["Todays Tasks", "Upcomming", "Completed"]
    
    @Namespace var animation
    
    var body: some View {
        
        ScrollView{
            VStack{
                HStack{
                    Spacer()
                    
                    Text(TaskStatus[0])
                        .font(.system(size: 30))
                        .fontWeight(.semibold)
                    
                    Button {
                        print("")
                    } label: {
                        Text("May 2022")
                            .foregroundColor(.black)
                            .padding(.horizontal)
                            .font(.system(size: 25))
                    }
                    
                    
                    Spacer()
                }
                .padding()
                .padding(.top)
                customBar()
                    .padding()
                ForEach(tasks,id: \.self){item in
                    Task(name: item.title ?? "Task Name", desc: item.desc ?? "Task Desc", image: item.icon ?? "sportscourt", image1: "clock", Time: "10", Colour: item.colour ?? "70d6ff", deadline: item.deadline ?? Date())
                        
                }
                
                
            }
            
            
        }
        .overlay(alignment: .bottom){
            HStack{
                Spacer()
                Button {
                    isPresented.toggle()
                    
                } label: {
                    Image(systemName: "plus.square")
                        .font(.system(size: 50))
                        .foregroundColor(.black)
                }
                .fullScreenCover(isPresented: $isPresented, content: AddTask.init)
                .padding()
            }
            
            .padding(.horizontal)
        }
    }
    
    func deleteTask(at offsets: IndexSet) {
        for offset in offsets {
            let tasky = tasks[offset]
            moc.delete(tasky)
        }
        try?
        moc.save()
        
    }
    
    @ViewBuilder
    func customBar()-> some View{
        let tabs = ["Today", "Upcoming"," Completed"]
        HStack(spacing:10){
            ForEach(tabs,id: \.self){tab in
                Text(tab)
                    .font(.system(size: 20))
                    .scaleEffect(0.9)
                    .foregroundColor(taskModel.currentTab == tab ? .white:.black)
                    .padding(.vertical,6)
                    .frame(maxWidth:.infinity)
                    .background{
                        if taskModel.currentTab == tab{
                            Capsule()
                                .fill(Color(hex: "ff006e"))
                                .matchedGeometryEffect(id: "TAB", in: animation)
                        }
                    }
                    .contentShape(Capsule())
                    .onTapGesture{
                        withAnimation{taskModel.currentTab = tab}
                    }
            }
        }
    }
    
}


struct TaskView_Previews: PreviewProvider {
    static var previews: some View {
        TaskView()
            .previewDevice(PreviewDevice(rawValue: "iPhone 13"))
        TaskView()
            .previewDevice(PreviewDevice(rawValue: "iPhone 8"))
    }
}

extension Color {
    init(hex: String) {
        let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int: UInt64 = 0
        Scanner(string: hex).scanHexInt64(&int)
        let a, r, g, b: UInt64
        switch hex.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            (a, r, g, b) = (1, 1, 1, 0)
        }
        
        self.init(
            .sRGB,
            red: Double(r) / 255,
            green: Double(g) / 255,
            blue:  Double(b) / 255,
            opacity: Double(a) / 255
        )
    }
}

我希望有人能够为我指明我需要做什么的正确方向。

非常感谢

swift swiftui
1个回答
4
投票

这是我的非列表自定义 SwipeAction 版本:

与原版接近,只是无法在容器外点击取消。欢迎任何改进或建议。

用途:

        ItemView(item)
            .mySwipeAction { // red + trash icon as default
                deleteMyItem(item)
            }

        ItemView(item)
            .mySwipeAction(color: .green, icon: "flag" ) { // custom color + icon
                selectMyItem(item)
            }

示例:

代码:

extension View {
    func mySwipeAction(color: Color = .red,
                       icon: String = "trash",
                       action: @escaping () -> ()) -> some View {
        return self.modifier(MySwipeModifier(color: color, icon: icon, action: action ))
    }
}


struct MySwipeModifier: ViewModifier {
    
    let color: Color
    let icon: String
    let action: () -> ()
    
    @AppStorage("MySwipeActive") var mySwipeActive = false
    
    @State private var contentWidth: CGFloat = 0
    @State private var isDragging: Bool = false
    @State private var isDeleting: Bool = false
    @State private var isActive: Bool = false
    @State private var dragX: CGFloat = 0
    @State private var iconOffset: CGFloat = 40
    
    let miniumDistance: CGFloat = 20
    
    func body(content: Content) -> some View {
        ZStack(alignment: .trailing) {
            
            content
                .overlay( GeometryReader { geo in Color.clear.onAppear { contentWidth = geo.size.width }})
                .offset(x: -dragX)
            
            Group {
                color
                Image(systemName: icon)
                    .symbolVariant(.fill)
                    .foregroundColor(.white)
                    .offset(x: isDeleting ? 40 - dragX/2 : iconOffset)
            }
            .frame(width: max(dragX, 0))
            // tap on red area after being active > action
            .onTapGesture {
                withAnimation { action() }
            }
            
        }
        .contentShape(Rectangle())
        
        // tap somewhere else > deactivate
        .onTapGesture {
            withAnimation {
                isActive = false
                dragX = 0
                iconOffset = 40
                mySwipeActive = false
            }
        }
        
        .gesture(DragGesture(minimumDistance: miniumDistance)
                 
            .onChanged { value in
                
                // if dragging started new > reset dragging state for all (others)
                if !isDragging && !isActive {
                    mySwipeActive = false
                    isDragging = true
                }
                
                if value.translation.width < 0 {
                    dragX = -min(value.translation.width + miniumDistance, 0)
                } else if isActive {
                    dragX = max(80 - value.translation.width + miniumDistance, -30)
                }
                
                iconOffset = dragX > 80 ? -40+dragX/2 : 40-dragX/2
                withAnimation(.easeOut(duration: 0.3)) { isDeleting = dragX > contentWidth*0.75 }
                
                // full drag > action
                if value.translation.width <= -contentWidth {
                    withAnimation { action() }
                    mySwipeActive = false
                    isDragging = false
                    isActive = false
                    return
                }
                
            }
                 
            .onEnded { value in
                withAnimation(.easeOut) {
                    isDragging = false
                    
                    // half drag > change to active / show icon
                    if value.translation.width < -60 && !isActive {
                        isActive = true
                        mySwipeActive = true
                    } else {
                        isActive = false
                        mySwipeActive = false
                    }
                    
                    // in delete mode > action
                    if isDeleting { action() ; return }
                    
                    // in active mode > show icon
                    if isActive {
                        dragX = 80
                        iconOffset = 0
                        return
                    }
                    
                    dragX = 0
                    isDeleting = false
                }
            }
        )
        
        // reset all if swipe in other cell
        .onChange(of: mySwipeActive) { newValue in
            print("changed", newValue)
            if newValue == false && !isDragging {
                withAnimation {
                    dragX = 0
                    isActive = false
                    isDeleting = false
                    iconOffset = 40
                }
            }
        }
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.