I'm building a WatchOS-app (SwiftUI) with multiple pickers, but as soon as I add them to a ScrollView I can no longer simply tap a picker to select it.
When I tap a picker the first picker on the screen gets selected and I have to tap once more to have the right picker selected. Once I've double tapped the picker I can select other pickers just fine, but as soon as I tap outside to deselect all pickers I have to double tap again.
Sorry if the explanation is a bit fuzzy. This video shows the issue: Video
I'm new to both programming and Swift, so be gentle ;)
import SwiftUI
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct ContentView: View {
let paceArray = Array(0...59)
let speedArray = Array(0...99)
@State private var globalSecondsPerKM: Double = 0
@State private var paceMKMHours: Int = 0
@State private var paceMKMMinutes: Int = 0
@State private var paceMKMSeconds: Int = 0
@State private var paceMMHours: Int = 0
@State private var paceMMMinutes: Int = 0
@State private var paceMMSeconds: Int = 0
@State private var speedKMHWhole: Int = 0
@State private var speedKMHDecimal: Int = 0
@FocusState private var paceMKMFocused: Bool
@FocusState private var paceMMFocused: Bool
@FocusState private var speedKMHFocused: Bool
var body: some View {
ScrollView {
VStack {
VStack {
Text("Pace per km")
.font(.headline)
HStack {
Picker(selection: $paceMKMHours, label: Text(""), content: {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: "%02dh", speedArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMKMMinutes, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dm", paceArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMKMSeconds, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02ds", paceArray[index])).tag(index)
}
})
.frame(width: 45)
}
.focused($paceMKMFocused)
.padding(.bottom, 5)
.frame(height: 35)
}
Divider()
VStack {
Text("Pace per mile")
.font(.headline)
HStack {
Picker(selection: $paceMMHours, label: Text(""), content: {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: "%02dh", speedArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMMMinutes, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dm", paceArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $paceMMSeconds, label: Text(""), content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02ds", paceArray[index])).tag(index)
}
})
.frame(width: 45)
}
.focused($paceMMFocused)
.padding(.bottom, 5)
.frame(height: 35)
}
Divider()
VStack {
Text("Speed in km/h")
.font(.headline)
HStack {
Picker(selection: $speedKMHWhole, label: Text(""), content: {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: "%02d", speedArray[index])).tag(index)
}
})
.frame(width: 45)
Picker(selection: $speedKMHDecimal, label: Text("")) {
ForEach(0..<speedArray.count, id: \.self) { index in
Text(String(format: ".%02d", speedArray[index])).tag(index)
}
}
.frame(width: 45)
}
.focused($speedKMHFocused)
.padding(.bottom, 5)
.frame(height: 35)
}
Divider()
}
}
.labelsHidden()
.font(.system(size: 13))
}
}
This looks like a SwiftUI bug. A possible workaround is setting up a tap gesture on the picker, which triggers a focus change. The initial animation is not perfect, but it looks fine after that.
import SwiftUI
@available(watchOSApplicationExtension 8.0, *)
struct ContentView: View {
let paceArray = Array(0...59)
@State private var paceMKMHours: Int?
@State private var paceMKMMinutes: Int?
@State private var paceMKMSeconds: Int?
@FocusState private var shouldFocusHours: Bool
@FocusState private var shouldFocusMinutes: Bool
@FocusState private var shouldFocusSeconds: Bool
var body: some View {
ScrollView {
VStack {
HStack {
Picker("hours", selection: $paceMKMHours, content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dh", paceArray[index])).tag(index)
}
})
.onTapGesture {
shouldFocusHours = true
}
.focused($shouldFocusHours)
Picker("minutes", selection: $paceMKMMinutes, content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02dm", paceArray[index])).tag(index)
}
})
.onTapGesture {
shouldFocusMinutes = true
}
.focused($shouldFocusMinutes)
Picker("seconds", selection: $paceMKMSeconds, content: {
ForEach(0..<paceArray.count, id: \.self) { index in
Text(String(format: "%02ds", paceArray[index])).tag(index)
}
})
.onTapGesture {
shouldFocusSeconds = true
}
.focused($shouldFocusSeconds)
}
.padding(.bottom, 5)
.frame(height: 35)
}
}
.labelsHidden()
.font(.system(size: 13))
}
}