简体   繁体   中英

Toggling from Picker to Image view causes an index out of range error in SwiftUI

I have a view that uses a button to toggle between a Picker and an Image that is a result of the Picker selection. When quickly toggling from the image to the Picker and immediately back, I get a crash with the following error:

Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range

Toggling less quickly doesn't cause this, nor does toggling in the other direction (picker to image and back). Here is the offending code:

import SwiftUI

struct ContentView: View {
    @State private var showingPicker = false
    @State private var currentNum = 0
    @State private var numbers: [Int] = [1, 2, 3, 4, 5]
    
    
    var body: some View {
        
            VStack(spacing: 15) {
                Spacer()
                if showingPicker {
                    Picker("Number", selection: $currentNum) {
                        ForEach(0..<numbers.count, id: \.self) {
                            Text("\($0)")
                        }
                    }
                    .pickerStyle(.wheel)
                } else {
                    Image(systemName: "\(currentNum).circle")
                }
                
                Spacer()
                
                Button("Toggle") {
                    showingPicker = !showingPicker
                }
                
            }
    }
}

The code works otherwise. I'm new to SwiftUI so I'm still wrapping my head around how views are created/destroyed. I tried changing the order of the properties thinking maybe the array was being accessed before it was recreated(if that's even something that happens) but that had no effect. I also tried ForEach(numbers.indices) instead of ForEach(0..<numbers.count) , but it has the same result.

**Edit

I figured out a stop-gap for now. I added @State private var buttonEnabled = true and modified the button:

Button("Toggle") {
    showingPicker = !showingPicker
    buttonEnabled = false
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
        buttonEnabled = true
    }
}
.disabled(buttonEnabled == false)

To debounce it. I still want to figure out the problem and make a real fix.

**Edit

Based on comments I've modified the code to take array indexing out of the equation and to better reflect the actual project I'm working on. The code still works, but a quick toggle will cause the exact same crash and error. It also seems to only happen when the.wheel style picker is used, other picker styles don't have this behavior.

private let icons = ["ear", "cube", "eye", "forward", "gear"]

struct ContentView: View {
    @State private var showingPicker = false
    @State private var currentIcon = icons[0]
    
    var body: some View {
        VStack(spacing: 15) {
            Spacer()
            if showingPicker {
                Picker("Icon", selection: $currentIcon) {
                    ForEach(icons, id: \.self) {
                        Image(systemName: $0)
                    }
                }
                .pickerStyle(.wheel)
            } else {
                Image(systemName: currentIcon)
            }
            
            Spacer()
            
            Button("Toggle") {
                showingPicker.toggle()
            }
        }
    }
}

ForEach is not a for loop, you can't use array.count and id:\.self you need to use a real id param or use the Identifiable protocol.

However if you just need numbers it also supports this:

ForEach(0..<5) { i in

As long as you don't try to look up an array using i .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM