In AppKit I would do this by assigning its key equivalent to be ↩ or making its cell the window's default. However, neither of these seems possible in SwiftUI, so how do I make a button the default window button?
SwiftUI's button is similar to UIButton , except it's more flexible in terms of what content it shows and it uses a closure for its action rather than the old target/action system. To create a button with a string title you would start with code like this: Button("Button title") { print("Button tapped!") }
For example: you can run actionOfButton() from the place you want programmatically tap the Button, it would work the same.
SwiftUI lets us disable any part of its forms or even the whole form, all by using the disabled() modifier. This takes a single Boolean that defines whether the element should be disabled or not. The form element's style automatically gets updated to reflect its status – buttons and toggles get grayed out, for example.
We are going to create two @State var's called circleTapped and circlePressed. Then we are going to create a ZStack. Here we are going to be creating an Image for our button this image is going to be used from the SF Symbols app. Now that is done, we are now going to give the button a background.
As of Xcode 12 beta, new methods are exposed on Button() allowing assignment of keyEquivalent (either by enum case or explicit key and modifiers).
Setting as default:
Button( ... )
.keyboardShortcut(.defaultAction)
Setting as cancel:
Button( ... )
.keyboardShortcut(.cancelAction)
It's currently not possible. I have reported it to Apple.
However, for now, you can wrap NSButton.
Usage:
@available(macOS 10.15, *)
struct ContentView: View {
var body: some View {
NativeButton("Submit", keyEquivalent: .return) {
// Some action
}
.padding()
}
}
Implementation:
// 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()
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With