简体   繁体   English

使用 SwiftUI 创建手势以编辑列表项

[英]Create gesture to edit list item using SwiftUI

I am trying to implement a swipe(from left to the right) to edit action using SwiftUI.我正在尝试使用 SwiftUI 实现滑动(从左到右)来编辑操作。 A delete action(swipe from right to left) and a move item action works perfectly.删除操作(从右向左滑动)和移动项目操作完美运行。

I want to open the edit screen on the left to the right guesture我想打开左边到右边的编辑屏幕

This is my code:这是我的代码:

struct TableView : View {
@State var dataSource = DataSource()

var body: some View {
        NavigationView {
            List {
                ForEach(dataSource.pokemons.identified(by: \.id)) { pokemon in
                    Text(pokemon.name) 
                }
                .onDelete(perform: deletePokemon)
                .onMove(perform: movePokemon)
            }
            .navigationBarItems(leading: EditButton(), trailing: Button(action: addPokemon, label: { Text("Add") }))
            .navigationBarTitle(Text("Pokemons"))
        }
}

I don't think it is possible currently.我认为目前不可能。

The best suggestion I have is to roll your own solution by using UITableView via the UIViewRepresentable protocol.我的最佳建议是通过UIViewRepresentable协议使用UITableView推出您自己的解决方案。 That being said, there might be viable open-source solutions out there.话虽如此,那里可能有可行的开源解决方案。

I think hoping for all the UITableView features you may want is risky because List is supposed to be a "generic" type that is supported across various platforms.我认为希望拥有您可能想要的所有UITableView功能是有风险的,因为List应该是跨各种平台支持的“通用”类型。 Some features of UITableView may never come to a List . UITableView某些功能可能永远不会出现在List

This is quick code I typed up, but it gives a simple example of how to create a custom UITableView solution:这是我输入的快速代码,但它提供了一个如何创建自定义UITableView解决方案的简单示例:

RoutineTableView(routines: routineDataSource.routines)
  .trailingSwipeActionsConfiguration {
    let editAction = UIContextualAction(
      style: .normal,
      title: "EDIT"
    ) { (action, sourceView, completionHandler) in

      completionHandler(true)
    }
    editAction.backgroundColor = UIColor.darkGray
    let deleteAction = UIContextualAction(
      style: .destructive,
      title: "DELETE"
    ) { (action, sourceView, completionHandler) in

      completionHandler(true)
    }
    let actions = [deleteAction, editAction]
    let configuration = UISwipeActionsConfiguration(actions: actions)
    return configuration
  }
  .onCellPress {
    print("hi there")
  }
  .navigationBarTitle("Routines")
private class CustomDataSource<SectionType: Hashable, ItemType: Hashable>: UITableViewDiffableDataSource<SectionType, ItemType> {

  override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
  }
}

struct RoutineTableView: UIViewRepresentable {

  let routines: [Routine]
  private var onCellPress: (() -> Void)? = nil
  private var trailingSwipeActionsConfiguration: (() -> UISwipeActionsConfiguration)? = nil

  init(routines: [Routine]) {
    self.routines = routines
  }

  func makeUIView(
    context: UIViewRepresentableContext<RoutineTableView>
  ) -> UITableView {
    let tableView = UITableView()
    context.coordinator.update(withTableView: tableView)
    return tableView
  }

  func updateUIView(_ uiView: UITableView, context: UIViewRepresentableContext<RoutineTableView>) {
    context.coordinator.update(routines: routines)
  }

  // MARK: - Coordinator

  func makeCoordinator() -> RoutineTableView.Coordinator {
    return Coordinator(self)
  }

  class Coordinator: NSObject, UITableViewDelegate {

    private enum Section {
      case first
    }

    private let view: RoutineTableView
    private var dataSource: UITableViewDiffableDataSource<Section, Routine>?

    init(_ view: RoutineTableView) {
      self.view = view
      super.init()
    }

    func update(withTableView tableView: UITableView) {
      tableView.register(RoutineTableViewCell.self)
      tableView.delegate = self

      let dataSource = CustomDataSource<Section, Routine>(tableView: tableView) { (tableView, indexPath, routine) -> UITableViewCell? in
        let cell: RoutineTableViewCell = tableView.dequeueReusableCell(for: indexPath)
        cell.configure(withRoutine: routine)
        return cell
      }
      self.dataSource = dataSource
    }

    func update(routines: [Routine]) {
      var snapshot = NSDiffableDataSourceSnapshot<Section, Routine>()
      snapshot.appendSections([.first])
      snapshot.appendItems(routines)
      dataSource?.apply(snapshot, animatingDifferences: true)
    }

    // MARK: - <UITableViewDelegate>

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      view.onCellPress?()
    }

    func tableView(
      _ tableView: UITableView,
      trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
    ) -> UISwipeActionsConfiguration? {
      return view.trailingSwipeActionsConfiguration?()
    }

  }
}

extension RoutineTableView {

  func onCellPress(
    _ onCellPress: @escaping () -> Void
  ) -> RoutineTableView {
    var view = self
    view.onCellPress = onCellPress
    return view
  }

  func trailingSwipeActionsConfiguration(
    _ trailingSwipeActionsConfiguration: @escaping () -> UISwipeActionsConfiguration
  ) -> RoutineTableView {
    var view = self
    view.trailingSwipeActionsConfiguration = trailingSwipeActionsConfiguration
    return view
  }
}

You have to use EditButton() instead.您必须改用 EditButton()。 It enables edit mode for a List component.它为 List 组件启用编辑模式。

Wow!哇! Hmmmm, I'm not sure about using an EditButton() !嗯,我不确定是否使用 EditButton() !

I assume you have a list and you want to swipe the row and see a choice to delete right?我假设您有一个列表,并且想要滑动该行并查看删除选项,对吗?

All you have to do is implement .onDelete(perform: delete) after the closure for the List.您所要做的就是在 List 关闭后实现.onDelete(perform: delete) Then, add a function to the structure that defines the delete function in which you handle the closure.然后,向结构中添加一个函数,该函数定义了您在其中处理闭包的删除函数。 Remember that the function will be defined as: func delete (at offsets: IndexSet) {}请记住,该函数将定义为: func delete (at offsets: IndexSet) {}

Add what I've suggested and compile even without the function body completed (ie add a print() placeholder) and you can see the swipe behaviour for delete.添加我建议的内容并在没有完成函数体的情况下进行编译(即添加一个 print() 占位符),您可以看到删除的滑动行为。

iOS 15 iOS 15

Starting from iOS 15 you can use swipeActions :从 iOS 15 开始,您可以使用swipeActions

ForEach(dataSource.pokemons.identified(by: \.id)) { pokemon in
    Text(pokemon.name)
}
.swipeActions(edge: .leading) {
    Button("Edit") {
        print("Edit")
    }
    .tint(.blue)
}
.swipeActions(edge: .trailing) {
    Button("Delete", role: .destructive) {
        print("Delete")
    }
    Button("Flag") {
        print("Flag")
    }
    .tint(.orange)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM