iosswiftuiwidgetvoiceoverios17

Adding buttonStyle for SwiftUI Widget intent button messes up VoiceOver


In the SwiftUI Interactive widget(iOS 17), when you add:

Button(intent: callWidgetIntent) {
    HStack {
        Image(systemName: "eye.fill")
        Text("Update")
    }
}
.buttonStyle(.plain)

It will mess up VoiceOver. Reads out unpronounceable and action will lead to the app, not calling the intent. It can be any buttonStyle - not only .plain. Also adding accessibilityLabel doesn't help. When I'll remove the buttonStyle, then VoiceOver works.

Any idea why or how to fix it? One way is to remove style and add tint(.clear) to remove the button background:

Button(intent: callWidgetIntent) {
    HStack {
        Image(systemName: "eye.fill")
        Text("Update")
    }
    .foregroundColor(.white)
}
.tint(.clear)
.accessibilityElement(children: .ignore)
.accessibilityLabel("Update")

But in that case, there is some weird padding for the button. Need to remove it without introducing some magic negative number for padding


Solution

  • Solved the problem by creating a custom ButtonStyle for the widget. The most important is to have minWidth and minHeight bigger than content size. Then it needed to have .accessibilityElement(children: .ignore) or .accessibilityElement() And set accessibilityLabel. Not the best solution and not very dynamic, but haven't found better solution yet

    So it looks sth like:

    public struct WidgetButtonStyle: ButtonStyle {
        public func makeBody(configuration: Configuration) -> some View {
            configuration
                .label
                .tint(.clear)
                // minSize needs to be bigger than content
                .frame(minWidth: 30, minHeight: 30)
                // This is also needed to make VoiceOver work correctly
                .accessibilityElement()
                // Could add accessibilityLabel here also, but I decided to do it outside
        }
    }
    
    // Using it:
    Button(
        "My button",
        action: {}
    )
    .buttonStyle(WidgetButtonStyle())
    .accessibilityLabel("My button")