Change in behavior for .isToggle trait

One of my blind users reached out to me after they updated to iPadOS 18. I have a button that is used to mark/unmark a favorite location.

He said that he could still tell the favorite status via the label and hint, but VoiceOver was always saying that the switch button is off.

I'm able to recreate this on my iPad as well. The documentation for .isToggle is completely blank.

Basically with iPadOS 17 adding the .isToggle trait makes VoiceOver state that my button is a toggle.

With iPadOS 18 adding the .isToggle trait makes VoiceOver state that my button is a toggle AND try to state its current value. I don't know if this is intentional or a bug.

                    Button {
                        showFavActions()
                    } label: {
                        Image(systemName: SFSymbolShortcut.star.rawValue)
                            .symbolVariant(weatherData.currentlyFavIndex == nil ? .none : .fill)
                    }
                    .buttonStyle(.plain)
                    .accessibilityLabel(Text(weatherData.currentlyFavIndex == nil ? "Not a favorite location." : "Favorite location."))
                    .accessibilityHint(Text(weatherData.currentlyFavIndex == nil ? "Add to favorites." : "Remove from favorites."))
                    .accessibilityInputLabels(["Favorite"])
                    .accessibilityAddTraits(.isToggle) // iOS 17
Answered by shiftingsand in 801373022

I think I have a way to address this.

I changed the accessibilityLabel to be "Favorite Button" and added accessibilityValue with the current value.

After that VoiceOver is no longer always saying the toggle is off.

Button {
    showFavActions()
} label: {
    Image(systemName: SFSymbolShortcut.star.rawValue)
        .symbolVariant(weatherData.currentlyFavIndex == nil ? 
                       .none : .fill)
}
.buttonStyle(.plain)
.accessibilityLabel(Text("Favorite Button"))
.accessibilityHint(Text(weatherData.currentlyFavIndex == nil ? 
                        "Add to favorites." : 
                        "Remove from favorites."))
.accessibilityInputLabels(["Favorite"])
.accessibilityAddTraits(.isToggle) // iOS 17
.accessibilityValue(weatherData.currentlyFavIndex == nil ? 
                    "Not a favorite location." : 
                    "Favorite location.")

I think I have a way to address this.

I changed the accessibilityLabel to be "Favorite Button" and added accessibilityValue with the current value.

After that VoiceOver is no longer always saying the toggle is off.

Button {
    showFavActions()
} label: {
    Image(systemName: SFSymbolShortcut.star.rawValue)
        .symbolVariant(weatherData.currentlyFavIndex == nil ? 
                       .none : .fill)
}
.buttonStyle(.plain)
.accessibilityLabel(Text("Favorite Button"))
.accessibilityHint(Text(weatherData.currentlyFavIndex == nil ? 
                        "Add to favorites." : 
                        "Remove from favorites."))
.accessibilityInputLabels(["Favorite"])
.accessibilityAddTraits(.isToggle) // iOS 17
.accessibilityValue(weatherData.currentlyFavIndex == nil ? 
                    "Not a favorite location." : 
                    "Favorite location.")

@shiftingsand Glad you figured that out. To add, for custom views that function similarly to toggles, you can use the accessibilityAddTraits() modifier to explicitly define their accessibility traits as a toggle by using the .isToggle value.

For example:

var body: some View {
    Rectangle()
        .foregroundStyle(isOn ? .green : .red)
        .accessibilityLabel("My Toggle")
        .accessibilityValue(isOn ? "On" : "Off")
        .accessibilityAddTraits(.isToggle)
        .onTapGesture { isOn.toggle() }
}

I would suggest you please file an enhancement request via Feedback Assistant to have the documentation updated. Please post the feedback number here for the record and I will follow up on it.

Change in behavior for .isToggle trait
 
 
Q