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.