2

I'm trying to add Touch Bar support for a SwiftUI View. There seems to be SwiftUI API for this using the .touchBar(content: () -> View) function on Views, but documentation is non existent and I can't get my Touch Bar to display anything.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .touchBar {
                Button(action: {

                }) {
                    Text("do something")
                }
        }
    }
}

This code does compile and run, but the Touch Bar remains empty. How can I get my touch bar to display content using SwiftUI (not catalyst)?

Asperi
  • 228,894
  • 20
  • 464
  • 690
Blasmo
  • 458
  • 3
  • 18

2 Answers2

7

Using .focusable doesn't work without "Use keyboard navigation to move focus between controls" checked in System Preferences -> Keyboard -> Shortcuts. To work around that, I did this:

/// Bit of a hack to enable touch bar support.
class FocusNSView: NSView {
    override var acceptsFirstResponder: Bool {
        return true
    }
}

/// Gets the keyboard focus if nothing else is focused.
struct FocusView: NSViewRepresentable {

    func makeNSView(context: NSViewRepresentableContext<FocusView>) -> FocusNSView {
        return FocusNSView()
    }

    func updateNSView(_ nsView: FocusNSView, context: Context) {

        // Delay making the view the first responder to avoid SwiftUI errors.
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
            if let window = nsView.window {

                // Only set the focus if nothing else is focused.
                if let _ = window.firstResponder as? NSWindow {
                    window.makeFirstResponder(nsView)
                }
            }
        }
    }
}
Taylor
  • 5,871
  • 2
  • 30
  • 64
  • 1
    This works amazing! Just to add, you have to use FocusNSView() inside your View where touchBar modifier is used. I also use frame(width: 0, height: 0) on the FocusNSView! Thank you!! – davidev Jun 02 '20 at 07:06
  • 1
    I would be so happy if we would not need this kind of workarounds as of WWDC 2020 – Marc T. Jun 20 '20 at 06:51
3

Help from this How to use a SwiftUI touchbar with a NSWindow - Apple Developer Forums:

Use the focusable() modifier

The touch bar shows the text when you add the .focusable() modifier just before the .touchBar(content:) modifier.

struct ContentView: View {

    var body: some View {
        Text("Hello, World!")
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .focusable()
            .touchBar {
                Button(action: {
                    print("Perform some action")
                }) {
                    Text("do something")
                }
        }
    }
}

Touch bar image

George
  • 25,988
  • 10
  • 79
  • 133
  • Thank you for helping. I ran this code, but the Touch Bar remains empty. Did this code work for you? – Blasmo Jan 26 '20 at 15:18
  • @Vincent It did, appeared just on the left next to `esc`. I added an image to the answer. – George Jan 26 '20 at 15:20
  • 2
    Thank you very much! Apparently there was one more thing I needed to do to make it work. I needed to open `System Preferences` > `Keyboard` > `Shortcuts` and make sure 'Use keyboard navigation to move focus between controls' is checked. Seems like a SwiftUI bug (or at least very unexpected behaviour) :) – Blasmo Jan 26 '20 at 16:06
  • @Vincent Is there any workaround for that? The user won't have that activate maybe.. – davidev May 23 '20 at 10:08
  • I haven't found any workaround. I hope this get smoothened out in the next release of swiftUI – Blasmo May 29 '20 at 07:43