swiftuiforeachenumsaccessibilityonappear

SwiftUI Dynamically Create Enum or


I have a custom picker where the contents of the picker are created from a downloaded JSON file. In an effort to provide better accessibility for challenged users, I am trying to use the .accessibilityFocused modifier to focus VO on a user's previous selection. This means that when the user first views the picker it should start at the top, but if they go back in to change their selection, it should auto-focus on their previous selection instead of having them start over from the top of the list.

The issue is that to use .accessibilityFocused you really need to do so via an enum so you can call something like .accessibilityFocused($pickerAccessFocus, equals: .enumValue). As I am using a ForEach loop to create the picker based off of the array that is created when the JSON is parsed and then stored in the struct, I haven't figured out how to create the various enum cases based off of that array.

So, to recap:

  1. Inside the ForEach loop, I need to have a .accessibilityFocused modifier identifying each of the picker options

  2. The onAppear needs to say something along the lines of...

    if salutation == salutation.salutation {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            pickerAccessFocus = .ENUMCASE
        }
    } else {
        pickerAccessFocus = Optional.none
    }

Though apparently onAppear does not like to have a ForEach statement, so I'm not sure how to solve that issue either.

I know there's a lot of code in the example I provided, but I tried to combine it all into as simple as example as possible. Hoping it makes sense.

If I'm thinking about it wrong, and there's a better solution, I'm all ears.

import SwiftUI

struct Picker: View {

    @AccessibilityFocusState var pickerAccessFocus: PickerAccessFocus?
    @State private var salutation = ""
    var salutationList: [SalutationOptions] = [] // SalutationOptions is the struct from the parsed JSON

    enum PickerAccessFocus: Hashable {
        case ? // These cases need to be dynamically created as the same values the ForEach loop uses
    }

    static func nameSalutationPicker(name: String) -> LocalizedStringKey { LocalizedStringKey(String("NAME_SALUTATION_PICKER_\(name)")) }

    var body: some View {

        List {
            Section {
                ForEach(salutationList, id: \.id) { salutation in

                    HStack {
                        Text(nameSalutationPicker(name: salutation.salutation))

                    } // End HStack
                    .contentShape(Rectangle())
                    .accessibilityFocused(salutation == salutation.salutation ? ($pickerAccessFocus, equals: .ENUMCASE) : ($pickerAccessFocus, equals: Optional.none))
                } // End ForEach

            } // End Section

        } // End List

        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                pickerAccessFocus = .ENUMCASE
            }
        }
    }
}

Solution

  • You're making it much more complicated than necessary. You don't need an enum, you can use any value, even your array values directly.

    struct ContentView: View {
        
        @AccessibilityFocusState var pickerAccessFocus: SalutationOptions?
        @State private var salutation = ""
        var salutationList: [SalutationOptions] = []
        
        var body: some View {
            
            List {
                Section {
                    ForEach(salutationList, id: \.id) { salutation in
                        
                        HStack {
                            Text(salutation.salutation)
                            
                        } // End HStack
                        .contentShape(Rectangle())
                        .accessibilityFocused($pickerAccessFocus, equals: salutation)
                    } // End ForEach
                    
                } // End Section
                
            } // End List
        }
    }