ios – Tapping Button in ForEach always selects last button


I am using a class to store an array of colors and persist the index of that array corresponding to a user selected color in UserDefaults.

class AccentColorManager: ObservableObject {
    
    let colors: [Color] = [.orange, .yellow, .green, .blue, .purple, .brown, .cyan, .indigo, .mint, .teal]

    @Published var accentColor: Color = .orange // on initial launch orange is default color

    @Published var accentColorIndex: Int = 0 {
        didSet {
            accentColor = colors[accentColorIndex]
            saveAccentColorIndex()
            objectWillChange.send()
        }
    }

    init() {
        loadAccentColorIndex()
    }
    
    private let accentColorIndexKey = "AccentColorIndex"
    
    private func saveAccentColorIndex() {
        UserDefaults.standard.set(accentColorIndex, forKey: accentColorIndexKey)
    }

    private func loadAccentColorIndex() {
        if let storedIndex = UserDefaults.standard.value(forKey: accentColorIndexKey) as? Int {
            accentColorIndex = storedIndex
        }
    }
}

The view below presents the user with a list of colors. Regardless of what color the user selects, the very last list item is always selected, and subsequent selections have no effect.

struct ActiveColorSectionView: View {
    @EnvironmentObject var accentColorManager: AccentColorManager

    var body: some View {
        Section(header: Text("Active Color")) {
            VStack(alignment: .leading) {
                ForEach(accentColorManager.colors.indices, id: \.self) { index in
                    Button(action: {
                        accentColorManager.accentColorIndex = index
                    }) {
                        HStack {
                            Text(accentColorManager.colors[index].description)
                            Spacer()
                            Circle()
                                .frame(width: 20, height: 20)
                                .foregroundColor(accentColorManager.accentColorIndex == index ? accentColorManager.colors[index] : .clear)
                                .overlay(
                                    Circle()
                                        .stroke(Color.primary, lineWidth: 2)
                                )
                        }
                    }
                    .foregroundColor(accentColorManager.colors[index])
                    .padding(.vertical, 5)
                }
            }
        }
        .listRowBackground(Color(UIColor.systemBackground))
    }
}

I suspect that my use of using an index array may be problematic here, but I cannot see the specific problem and this seems more appropriate than saving a Color type to UserDefaults.

Additionally, I am using the AccentColorManager class as an environment variable:

@main
struct my_app: App {
    @StateObject private var accentColorManager = AccentColorManager()

    var body: some Scene {
        WindowGroup {
            WelcomeView()
                .environmentObject(accentColorManager)
        }
    }
}

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img