Mac上的SwiftUI-如何将按钮指定为主按钮?

时间:2019-07-31 05:40:45

标签: macos swiftui

在AppKit中,我可以通过assigning its key equivalent to be making its cell the window's default进行此操作。但是,在SwiftUI中,这两种方法似乎都不可行,因此如何将按钮设置为默认窗口按钮?

A Cancel button in the normal grey look, and a Save button in the system accent color. The text "Like this" is inserted with an arrow pointing to the Save button.

3 个答案:

答案 0 :(得分:11)

macOS 10.16 / iOS 14:

从Xcode 12 beta开始,在Button()上公开了新方法,允许分配keyEquivalent(按枚举大小写或显式键和修饰符)。

设置为默认值:

<form:form commandName="fooBar" .../>

设置为取消:

Button( ... )
    .keyboardShortcut(.defaultAction)

答案 1 :(得分:4)

当前不可能。我有reported it to Apple

但是,到目前为止,您可以包装NSButton。

用法:

@available(macOS 10.15, *)
struct ContentView: View {
    var body: some View {
        NativeButton("Submit", keyEquivalent: .return) {
            // Some action
        }
            .padding()
    }
}

实施:

// MARK: - Action closure for controls

private var controlActionClosureProtocolAssociatedObjectKey: UInt8 = 0

protocol ControlActionClosureProtocol: NSObjectProtocol {
    var target: AnyObject? { get set }
    var action: Selector? { get set }
}

private final class ActionTrampoline<T>: NSObject {
    let action: (T) -> Void

    init(action: @escaping (T) -> Void) {
        self.action = action
    }

    @objc
    func action(sender: AnyObject) {
        action(sender as! T)
    }
}

extension ControlActionClosureProtocol {
    func onAction(_ action: @escaping (Self) -> Void) {
        let trampoline = ActionTrampoline(action: action)
        self.target = trampoline
        self.action = #selector(ActionTrampoline<Self>.action(sender:))
        objc_setAssociatedObject(self, &controlActionClosureProtocolAssociatedObjectKey, trampoline, .OBJC_ASSOCIATION_RETAIN)
    }
}

extension NSControl: ControlActionClosureProtocol {}

// MARK: -



@available(macOS 10.15, *)
struct NativeButton: NSViewRepresentable {
    enum KeyEquivalent: String {
        case escape = "\u{1b}"
        case `return` = "\r"
    }

    var title: String?
    var attributedTitle: NSAttributedString?
    var keyEquivalent: KeyEquivalent?
    let action: () -> Void

    init(
        _ title: String,
        keyEquivalent: KeyEquivalent? = nil,
        action: @escaping () -> Void
    ) {
        self.title = title
        self.keyEquivalent = keyEquivalent
        self.action = action
    }

    init(
        _ attributedTitle: NSAttributedString,
        keyEquivalent: KeyEquivalent? = nil,
        action: @escaping () -> Void
    ) {
        self.attributedTitle = attributedTitle
        self.keyEquivalent = keyEquivalent
        self.action = action
    }

    func makeNSView(context: NSViewRepresentableContext<Self>) -> NSButton {
        let button = NSButton(title: "", target: nil, action: nil)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setContentHuggingPriority(.defaultHigh, for: .vertical)
        button.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        return button
    }

    func updateNSView(_ nsView: NSButton, context: NSViewRepresentableContext<Self>) {
        if attributedTitle == nil {
            nsView.title = title ?? ""
        }

        if title == nil {
            nsView.attributedTitle = attributedTitle ?? NSAttributedString(string: "")
        }

        nsView.keyEquivalent = keyEquivalent?.rawValue ?? ""

        nsView.onAction { _ in
            self.action()
        }
    }
}

答案 2 :(得分:0)

这是一个简短但通用性较低的解决方案,用于创建具有等效的返回键和默认按钮蓝色色调的主按钮。

struct PrimaryButtonView: NSViewRepresentable {
    typealias NSViewType = PrimaryButton

    let title: String
    let action: () -> Void

    init(_ title: String, action: @escaping () -> Void) {
        self.title = title
        self.action = action
    }

    func makeNSView(context: Context) -> PrimaryButton {
        PrimaryButton(title, action: action)
    }

    func updateNSView(_ nsView: PrimaryButton, context: Context) {
        return
    }
}


class PrimaryButton: NSButton {
    let buttonAction: () -> Void

    init(_ title: String, action: @escaping () -> Void) {
        self.buttonAction = action
        super.init(frame: .zero)
        self.title = title
        self.action = #selector(clickButton(_:))
        bezelStyle = .rounded  //Only this style results in blue tint for button
        isBordered = true
        focusRingType = .none
        keyEquivalent = "\r"
    }

    required init?(coder: NSCoder) {
        fatalError()
    }

    @objc func clickButton(_ sender: PrimaryButton) {
        buttonAction()
    }
}