Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - Making a View focusable and trigger an action on macOS

How is it possible to make a View on macOS focusable. I tried the it like below but the action is never triggered. I know that for NSView you have to implement acceptsFirstResponder to return true but can not find similar in SwiftUI.

Is this still a Beta related bug or a missing functionality for macOS ?

struct FocusableView: View {

    var body: some View {
        Group {
            Text("Hello World!")
                .padding(20)
                .background(Color.blue.cornerRadius(8))
            .focusable(true) { isFocused in
                print("Focused", isFocused)
            }
        }
    }
}

like image 646
Marc T. Avatar asked Sep 22 '19 12:09

Marc T.


1 Answers

I think I found a work around for the problem to shift the focus to a SwiftUI view. I am working on macOS Catalina, 10.15, Swift 5, Xcode 11.1

The problem is to shift the focus to a SwiftUI view. I could imagine that this has not been perfectly implemented, yet.

Only upon shifting the focus to the required SwiftUI view within the SwiftUI framework the onFocusChange closure of a focusable view will be called and the view will be focused.

My intention was: when I click into the SwiftUI view I want to execute the onFocusChange closure and I want it to be focused (for subsequent paste commands)

My SwiftUI view is built into the Window using an NSHostingView subclass - ImageHostingView.

  1. When I add this ImageHostingView to the view hierarchy I define its previous key view:

    theImageHostingView.superview.nextKeyView = theImageHostingView
    
  2. I added a mouseDown handler to the ImageHostingView:

    override dynamic func mouseDown(with event: NSEvent) {
      if let theWindow = self.window, let theView = self.previousValidKeyView {
      theWindow.selectKeyView(following:theView)
     }
    }
    

Calling the NSWindow's selectKeyView(following:) triggers within the SwiftUI framework a focus change to the desired view (the root view of ImageHostingView).

This is clearly a work-around and works only for final views which will be represented by a NSHostingView. But it highlights the problem (shifting the focus to a SwiftUI view upon a certain action) and it might be helpful in many cases.

like image 188
M Wilm Avatar answered Oct 14 '22 16:10

M Wilm