I am currently working on a settings menu and was hoping to use SwiftUI for it. It has all been working swimmingly until recently I tested it on an older iOS version, 17.5 and 17.7 and then the simultaneous gesture recognition stopped working and it could not scroll by moving on the rows. Here is the code that is producing the effects:
struct SettingsList: View {
var body: some View {
ScrollView(.vertical) {
VStack(spacing: 17.5) {
SettingsSection(content: [
SettingsRow(title: "A"),
SettingsRow(title: "B"),
SettingsRow(title: "C"),
SettingsRow(title: "D")
])
.padding(.top, 10)
SettingsSection(content: [
SettingsRow(title: "E", showsChevron: false)
])
SettingsSection(content: [
SettingsRow(title: "F"),
SettingsRow(title: "G"),
SettingsRow(title: "H"),
SettingsRow(title: "I")
])
SettingsSection(content: [
SettingsRow(title: "J")
])
SettingsSection(content: [
SettingsRow(title: "K"),
SettingsRow(title: "L"),
SettingsRow(title: "M"),
SettingsRow(title: "N"),
SettingsRow(title: "O")
])
SettingsSection(content: [
SettingsRow(title: "P"),
SettingsRow(title: "Q")
])
.padding(.top, 25)
}
.padding(.vertical, 10)
}
}
}
struct SettingsSection: View {
let content: [SettingsRow]
init(content: [SettingsRow]) {
self.content = content
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
ZStack {
VStack(spacing: 0) {
ForEach(Array(content.indices), id: \.self) { index in
content[index]
}
}
VStack(spacing: SettingsRow.height) {
ForEach(0...content.count, id: \.self) { index in
if index > 0 && index != content.count {
Divider()
.padding(.leading, 15)
.frame(height: 0)
} else {
Divider()
.frame(height: 0)
}
}
}
}
.background(Color.white)
}
}
}
struct SettingsRow: View {
static let height: CGFloat = 53
var title: String
var showsChevron = true
var onClick: () -> Void
@GestureState private var isPressed = false
@State private var isCancelled = false
private let selectionColor = Color(red: 210.0 / 255.0, green: 209.0 / 255.0, blue: 214.0 / 255.0)
private let chevronColor = Color(red: 197.0 / 255.0, green: 197.0 / 255.0, blue: 199.0 / 255.0)
init(title: String, showsChevron: Bool = true, onClick: @escaping () -> Void = {}) {
self.title = title
self.showsChevron = showsChevron
self.onClick = onClick
}
var backgroundColor: Color {
return isPressed && !isCancelled ? Color.black.opacity(0.15) : Color.clear
}
var body: some View {
HStack(spacing: 0) {
Text(title)
.font(.custom("Avenir-Light", size: 17))
.foregroundColor(.black)
.padding(.trailing, 10)
Spacer()
if showsChevron {
Image(systemName: "chevron.right")
.font(.system(size: 13).weight(.semibold))
.foregroundColor(chevronColor)
}
}
.padding(.horizontal, 15)
.padding(.vertical, 12.5)
.frame(height: Self.height)
.contentShape(Rectangle()) // Makes the entire row tappable
.background(backgroundColor) // Darkens when pressed
.background(Color.white)
.animation(.easeInOut(duration: 0.1), value: isPressed)
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.updating($isPressed) { value, isPressed, _ in
// Check vertical movement threshold
if abs(value.translation.height) > 10 { // Vertical threshold
isCancelled = true
} else {
isCancelled = false
isPressed = true
}
}
.onEnded { value in
if !isCancelled && abs(value.translation.height) <= 10 {
onClick()
}
isCancelled = false
}
)
}
}
I wanted to make my code like this to be able to easily add sections and rows and felt SwiftUI was the best way to do so. If it is possible I would like to use it and if that means making a fallback implementation for iOS 17, that's okay.
In iOS 17 currently it only scrolls when tapping between the sections. I have tried numerous solutions including trying to make my own scroll view implementation and high priority gestures, all to no avail. How do I fix this for iOS 17?