在SwiftUI中删除LazyVStack和ForEach中的行

时间:2020-09-28 13:25:43

标签: ios swift xcode core-data swiftui

使用List可以简单地使用.onDelete修饰符从列表中删除行。但是,如何在ForEach中显示的LazyVStack中执行相同的操作。我正在使用SwipeCell施加拖动手势并显示一个删除按钮,但是实际上如何从CoreData中删除,特别是在这种情况下如何访问IndexSet

LazyVStack {
    ForEach(items) { item in
        Text("\(item.name)")
            .swipeCell(cellPosition: .right, leftSlot: nil, rightSlot: slot1)
            .alert(isPresented: $showAlert){
                Alert(title: Text("Delete Task?"), message: nil, primaryButton:.destructive(Text("Delete"), action: {


                               // HOW DO I DELETE HERE ?


                               dismissDestructiveDelayButton()
                            }),secondaryButton: .cancel({dismissDestructiveDelayButton()}))
                        }
     }
}

private func deleteItems(offsets: IndexSet) {
    withAnimation {
        offsets.map { items[$0] }.forEach(viewContext.delete)
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}

1 个答案:

答案 0 :(得分:3)

我制作了一个 ViewModifier,它允许您删除任何您想要的视图,类似于 Apple 动画。

使用时,只需在任何视图上调用 .onDelete { //perform deletion }

struct Delete: ViewModifier {
    
    let action: () -> Void
    
    @State var offset: CGSize = .zero
    @State var initialOffset: CGSize = .zero
    @State var contentWidth: CGFloat = 0.0
    @State var willDeleteIfReleased = false
   
    func body(content: Content) -> some View {
        content
            .background(
                GeometryReader { geometry in
                    ZStack {
                        Rectangle()
                            .foregroundColor(.red)
                        Image(systemName: "trash")
                            .foregroundColor(.white)
                            .font(.title2.bold())
                            .layoutPriority(-1)
                    }.frame(width: -offset.width)
                    .offset(x: geometry.size.width)
                    .onAppear {
                        contentWidth = geometry.size.width
                    }
                    .gesture(
                        TapGesture()
                            .onEnded {
                                delete()
                            }
                    )
                }
            )
            .offset(x: offset.width, y: 0)
            .gesture (
                DragGesture()
                    .onChanged { gesture in
                        if gesture.translation.width + initialOffset.width <= 0 {
                            self.offset.width = gesture.translation.width + initialOffset.width
                        }
                        if self.offset.width < -deletionDistance && !willDeleteIfReleased {
                            hapticFeedback()
                            willDeleteIfReleased.toggle()
                        } else if offset.width > -deletionDistance && willDeleteIfReleased {
                            hapticFeedback()
                            willDeleteIfReleased.toggle()
                        }
                    }
                    .onEnded { _ in
                        if offset.width < -deletionDistance {
                            delete()
                        } else if offset.width < -halfDeletionDistance {
                            offset.width = -tappableDeletionWidth
                            initialOffset.width = -tappableDeletionWidth
                        } else {
                            offset = .zero
                            initialOffset = .zero
                        }
                    }
            )
            .animation(.interactiveSpring())
    }
    
    private func delete() {
        offset.width = -contentWidth
        action()
    }
    
    private func hapticFeedback() {
        let generator = UIImpactFeedbackGenerator(style: .medium)
        generator.impactOccurred()
    }
    
    //MARK: Constants
    
    let deletionDistance = CGFloat(200)
    let halfDeletionDistance = CGFloat(50)
    let tappableDeletionWidth = CGFloat(100)
    
    
}

extension View {
    
    func onDelete(perform action: @escaping () -> Void) -> some View {
        self.modifier(Delete(action: action))
    }
    
}