macOS 上的 SwiftUI:如何为 onDelete 启用 UI(从列表中删除)

Posted

技术标签:

【中文标题】macOS 上的 SwiftUI:如何为 onDelete 启用 UI(从列表中删除)【英文标题】:SwiftUI on macOS: How to enable UI for onDelete (deletion from List) 【发布时间】:2020-01-16 08:05:54 【问题描述】:

也许我今天早上特别密集,但我试图在 macOS 上的 SwiftUI 中从 List 中删除一行。

问题是没有公开 UI 来执行删除。我的意思是List 不响应删除按键,没有右键菜单,也不支持任何其他手势,如滑动删除(无论如何这在 macOS 上很奇怪)。

这是我正在使用的示例:

import SwiftUI

struct ContentView: View 
    @State var items = ["foo", "bar", "baz"]
    @State var selection: String? = nil

    var body: some View 
        List(selection: $selection) 
            ForEach(items, id: \.self)  Text($0) 
                .onDelete  self.items.remove(atOffsets: $0)
        
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    

ios 上完全相同的代码为我提供了一个带有标准“向左滑动删除”UI 的表格视图。在 macOS 上什么都没有。

我尝试添加

    .onDeleteCommand(perform: 
        if let sel = self.selection, let idx = self.items.firstIndex(of: sel) 
            self.items.remove(at: idx)
        
    )

List,但仍然没有响应删除按键。

如何在 macOS 上启用List 行删除?

【问题讨论】:

我假设您必须明确添加编辑模式或向左侧滑动列表行。 嗯,我试过刷卡并添加.environment(\.editMode, .constant(.active))给我'editMode' is unavailable in macOS 是的,只需检查没有 EditMode,但滑动即可工作 - 右侧显示红色删除按钮,点击删除记录。 哦,我明白了。我正在拖动鼠标以模拟滑动,但神奇的鼠标手势有效。必须有一种方法可以让退格起作用吗?我本来希望这是默认设置。 【参考方案1】:

此代码启用“删除”菜单并在我选择“编辑”>“删除”时删除所选项目(无需手动连接菜单):

struct ContentView: View 
    @State var items = ["foo", "bar", "baz"]
    @State var selection: String? = nil

    var body: some View 
        List(selection: $selection) 
            ForEach(items, id: \.self)  Text($0) 
        
        .onDeleteCommand 
            if
                let sel = self.selection,
                let idx = self.items.firstIndex(of: sel) 
                print("delete item: \(sel)")
                self.items.remove(at: idx)
            
        
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .animation(.default)
    

然后,要使删除 起作用,请将其设置为等效于删除菜单选项的键盘: - 编辑 Main.storyboard - 选择编辑 > 删除 - 单击“等效密钥”字段 - 点击删除键。

运行应用程序,选择一个项目,按删除键,您的项目应该会消失。

【讨论】:

这看起来等同于我最初的问题。这两个版本的行为对您来说是否不同? 嗯,我想我绕了一圈你的原始代码的变体......但是,刚刚编辑了我的答案以添加如何将删除键连接到删除菜单项。有了它,我可以单击一行并按下删除键,它就会删除。所以我想简短的回答是“将删除键添加为相当于 Menu.storyboard 中的编辑 > 删除菜单的键盘”,但我会将汇编代码保留在适当的位置以便于复制/粘贴。【参考方案2】:

对于 macOS,我们使用右键单击并查看菜单选项,例如删除。您可以在列表中添加一个上下文菜单,例如

  List(selection: $selection) 
        ForEach(items, id: \.self)  item in
               Text(item) 
                   .contextMenu 
                       Button(action:  
                       // delete item in items array 
                       )
                       Text("Delete")
                       
                    
        
            
  

【讨论】:

【参考方案3】:

我找到了一个相当复杂的解决方案,我希望有更好的方法。

我所做的是:

将操作连接到现有的“删除”命令 创建发布选定菜单命令的“ObservableObject”Menu 将发布者传递给ContentView,以便它可以订阅并对更改采取行动

以下是两个相关文件:

首先,AppDelegate.swift

enum MenuCommand 
    case none
    case delete


class Menu: ObservableObject 
    @Published var item: MenuCommand = .none



@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate 

    var window: NSWindow!
    @ObservedObject var menu = Menu()

    func applicationDidFinishLaunching(_ aNotification: Notification) 
        // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView(menu: menu)

        // Create the window and set the content view. 
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.center()
        window.setFrameAutosaveName("Main Window")
        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    

    func applicationWillTerminate(_ aNotification: Notification) 
        // Insert code here to tear down your application
    

    @IBAction func delete(_ sender: Any) 
        print("delete menu")
        menu.item = .delete
    


ContentView.swift:

import SwiftUI

struct ContentView: View 
    @ObservedObject var menu: Menu
    @State var items = ["foo", "bar", "baz"]
    @State var selection: String? = nil

    var body: some View 
        List(selection: $selection) 
            ForEach(items, id: \.self)  Text($0) 
        
        .onReceive(
            self.menu.objectWillChange
                .receive(on: RunLoop.main))  _ in
            if
                case .delete = self.menu.item,
                let sel = self.selection,
                let idx = self.items.firstIndex(of: sel) 
                print("delete item: \(sel)")
                self.items.remove(at: idx)
            
        
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .animation(.default)
    

注意:不要忘记将“删除”菜单项连接到IBAction

【讨论】:

【参考方案4】:

我面临同样的问题,我发现两根手指滑动触发删除。自从我删除 onDelete() 中的元素后,它因致命错误而崩溃,我仍在想办法解决它:

索引超出范围

编辑:我将索引替换为元素,效果很好。

ForEach(elementArray.indices)  index in 
    // code... 

ForEach(elementArray)  element in 
    // code... 

而且效果很好:)

【讨论】:

以上是关于macOS 上的 SwiftUI:如何为 onDelete 启用 UI(从列表中删除)的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 2.0 在 macOS 上禁用窗口的缩放按钮

macOS 上的 SwiftUI 选择器

macOS 上的 Swiftui 表单标签对齐

如何检测何时点击 TextField? (MacOS 上的 SwiftUI)

SwiftUI:响应 macOS 上的应用程序终止

SwiftUI:更改 macOS 上的默认命令菜单