Some of my apps that use a LazyVStack and a sheet for detail views show an odd behaviour as of iOS 17 where the modal does not show on rows that are loaded lazily, i.e. that are not displayed initially. Tap gesture doesn't trigger the modal or only when scrolling further, respectively.
The test code below works as expected on a device running iOS 16 or lower. Use iOS 17 instead and scroll down to a section further down (3 or 4 onwards). Tapping on a number will most probably not trigger the modal immediately or at all. It might get triggered after a short delay when you scroll up or down again, though.
Can anyone confirm this issue? Did they change anything in how lazy content is handled in iOS 17? Any workaround I can use?
Thanks for any feedback.
import SwiftUI
struct ListView: View {
@State private var sections = [1, 2, 3, 4, 5, 6]
@State var number: ModalDetail?
var body: some View {
VStack(spacing: 0) {
ScrollView {
LazyVStack(spacing: 10, pinnedViews: .sectionHeaders) {
ForEach(sections, id: \.self) { section in
Section {
ForEach(1..<21) { number in
Text("\(number)")
.onTapGesture {
self.number = ModalDetail(body: "Section \(section) / \(number)")
}
}
} header: {
Text("\(section)")
.font(Font.title.lowercaseSmallCaps())
.fontWeight(.medium)
.frame(minWidth: 0, maxWidth: .infinity)
.padding(.vertical, 8)
.padding(.horizontal, 8)
.background(.gray)
}
.sheet(item: $number, content: { detail in
self.modal(detail: detail.body)
})
}
}
}
Spacer()
}
.font(.title)
}
func modal(detail: String) -> some View {
Text(detail)
}
}
struct ModalDetail: Identifiable {
var id: String {
return body
}
let body: String
}
“The SwiftUI cookbook for navigation” from WWDC 2022 has some relevant information:
Although you’re using
sheet
, notnavigationDestination
, both reasons apply to your scenario too. You need to attach the modifier to a view that’s not loaded lazily, and you need to attach it to just one view.You could move it out to the
ScrollView
:Or you could attach it to the
VStack
: