Note: I'd like the solution to work for iOS 15 as well.
With the following implementation, tapping on the stepper from iPhone (iOS 15.8 (physical device) as well as iOS 17.2 (simulator and canvas)) presents ModalView, instead of changing the stepper's value as one would expect.
It's a somewhat real-life example but still basic, as I felt that having a view with just a stepper would have made the problem unrealistically easy.
struct CategoryView: View {
@State private var modalIsPresented = false
@State private var stepperValue = 0
var body: some View {
List {
StepperRow(value: self.$stepperValue)
.onTapGesture {
modalIsPresented = true
}
}
.sheet(isPresented: $modalIsPresented) {
modalIsPresented = false
} content: {
ModalView()
}
}
}
struct StepperRow: View {
@Binding var value: Int
var body: some View {
VStack(alignment: .leading) {
Stepper(
"\(value) Name of the article",
value: $value,
in: 0...Int.max
)
Text("Item description, which could be long and I'd like to go under the stepper.")
.font(.caption)
}
}
}
What doesn't work: setting the stepper's style to .plain or BorderlessButtonStyle(), as might work for a button.
The following code is a working solution, though it's ugly.
struct CategoryView: View {
@State private var stepperValue = 0
var body: some View {
List {
StepperRow(value: self.$stepperValue)
}
}
}
struct StepperRow: View {
@Binding var value: Int
@State private var modalIsPresented = false
var body: some View {
ZStack(alignment: .leading) {
VStack(alignment: .leading) {
HStack {
Text("\(value) Name of the article")
Spacer()
Stepper(
"",
value: $value,
in: 0...Int.max
)
.labelsHidden()
.hidden()
}
Text("Item description, which could be long and I'd like to go under the stepper.")
.font(.caption)
}
.onTapGesture {
modalIsPresented = true
}
VStack(alignment: .leading) {
HStack {
Text("\(value) Name of the article")
.hidden()
Spacer()
Stepper(
"",
value: $value,
in: 0...Int.max
)
.labelsHidden()
}
Text("Item description, which could be long and I'd like to go under the stepper.")
.font(.caption)
.hidden()
}
}
.sheet(isPresented: $modalIsPresented) {
modalIsPresented = false
} content: {
ModalView()
}
}
}
Basically I've put the stepper above the view to which I've added the onTapGesture recognizer, but to do so I had to duplicate the view code, so that everything laid out correctly, and hide the appropriate subviews, so that VoiceOver would ignore the duplicates, and also because it felt right.
Can anyone come up with a better solution?