Как создать пользовательскую кнопку удаления без использования слайда для удаления, который поставляется с SwiftUI Я не использую список, просто используя цикл foreach

#swiftui

#swiftui

Вопрос:

Поскольку OnDelete и onMove являются функциями списка / формы, я не могу их использовать, когда у меня есть пользовательские интерфейсы без них. Я использовал VStack внутри ForEach. Я совсем новичок в SwiftUI и не уверен, как я могу реализовать пользовательский код для OnDelete и onMove.

Вот мой код:

 struct Trying: View {
    @State private var numbers = [0,1,2,3,4,5,6,7,8,9]
    var body: some View {
        NavigationView {
            VStack (spacing: 10) {
                ForEach(numbers, id: .self) { number in
                    VStack {
                        Text("(number)")
                    }
                    .frame(width: 50, height: 50)
                    .background(Color.red)
                }.onDelete(perform: removeRows)
            }
            .navigationTitle("Trying")
            .navigationBarItems(trailing: EditButton())
        }
    }
    
    func removeRows(at offsets: IndexSet) {
        numbers.remove(atOffsets: offsets)
    }
}
  

То, как это работает прямо сейчас:

введите описание изображения здесь

Ответ №1:

Вот простая демонстрация возможного подхода к реализации пользовательского удаления (конечно, с move это было бы сложнее из-за перетаскивания, но идея та же). Протестировано с Xcode 12 / iOS 14.

ДЕМОНСТРАЦИЯ

 struct DemoCustomDelete: View {
    @State private var numbers = [0,1,2,3,4,5,6,7,8,9]
    var body: some View {
        NavigationView {
            VStack (spacing: 10) {
                ForEach(numbers, id: .self) { number in
                    VStack {
                        Text("(number)")
                    }
                    .frame(width: 50, height: 50)
                    .background(Color.red)
                    .overlay(
                        DeleteButton(number: number, numbers: $numbers, onDelete: removeRows)
                    , alignment: .topTrailing)
                }.onDelete(perform: removeRows)
            }
            .navigationTitle("Trying")
            .navigationBarItems(trailing: EditButton())
        }
    }

    func removeRows(at offsets: IndexSet) {
        withAnimation {
            numbers.remove(atOffsets: offsets)
        }
    }
}

struct DeleteButton: View {
    @Environment(.editMode) var editMode

    let number: Int
    @Binding var numbers: [Int]
    let onDelete: (IndexSet) -> ()

    var body: some View {
        VStack {
            if self.editMode?.wrappedValue == .active {
                Button(action: {
                    if let index = numbers.firstIndex(of: number) {
                        self.onDelete(IndexSet(integer: index))
                    }
                }) {
                    Image(systemName: "minus.circle")
                }
                .offset(x: 10, y: -10)
            }
        }
    }
}
  

Комментарии:

1. -глядя на ваше решение, мне интересно, будет ли это работать с CoreData? У меня есть lazyHGrid, который показывает результаты запроса выборки. Я не смог понять, как получить рабочую функцию удаления. до того, как я переключился на lazyHGrid, я использовал простой список, и я мог удалить через — private func deleteEvent(offsets: IndexSet) { withAnimation { offsets.map { events[$ 0] }.forEach(moc.delete) сделайте { try moc.save() } catch { пусть NSError = ошибка как NSError FatalError(«Неразрешенная ошибка ( NSError), (NSError.userInfo)») } } }

Ответ №2:

Основываясь на ответе @ Asperi, я просто обобщил его, чтобы принять любую Equatable последовательность.

 struct DeleteButton<T>: View where T: Equatable {
  @Environment(.editMode) var editMode

  let number: T
  @Binding var numbers: [T]
  let onDelete: (IndexSet) -> ()

  var body: some View {
    VStack {
        if self.editMode?.wrappedValue == .active {
            Button(action: {
                if let index = numbers.firstIndex(of: number) {
                    self.onDelete(IndexSet(integer: index))
                }
            }) {
                Image(systemName: "minus.circle")
            }
            .offset(x: 10, y: -10)
        }
    }
  }
}
  

Ответ №3:

Недавно мне понадобилось удалить строку, и я не мог использовать СПИСОК. Вместо этого у меня был вид прокрутки… Но я смог реализовать редактирование для имитации того же поведения при удалении, как если бы это был список. Изначально я не мог заставить свой код работать. Только после того, как я внимательно изучил свою реализацию и поэкспериментировал, я наткнулся на то, почему моя сработала. Я кодирую для iPad, поэтому мой NavigationView использует,

.navigationViewStyle (StackNavigationViewStyle())

Как только я добавил это в NavigationView структуры, при нажатии на кнопку редактирования активируется режим редактирования?.wrappedValue to .active / .inactive

Ниже приведена моя реализация для примера кода выше…

 struct Trying: View {
    
    @State var num: Int = 0
    @Environment(.editMode) var editMode
    
    @State private var numbers = [0,1,2,3,4,5,6,7,8,9]
    var body: some View {
        NavigationView {

            VStack {
                ForEach(numbers, id: .self) { number in
                    HStack {
                        if editMode?.wrappedValue == .active {
                            Button(action: { num = number
                                removeRows(numbr: num)
                            }, label: {
                                Image(systemName: "minus.circle.fill")
                                    .foregroundColor(.red)
                            })
                        } // END IF editMode?wrappedValue == .active
                        Text("(number)")
                            .frame(width: 50, height: 50)
                            .background(Color.red)
                    }

                }
//                .onDelete(perform: removeRows)
            }

            .navigationTitle("Trying")
            .navigationBarItems(trailing: EditButton())
        }
        // FOR SOME REASON THIS ALLOWS THE EditButton() to activate editMode  without a LIST being present.
        .navigationViewStyle(StackNavigationViewStyle())
    }
    
    func removeRows(numbr: Int) {
        print("removing (numbr)")
    }
}

  

Это выглядит как:

Моделирование режима редактирования