Trying to programmatically determine when an item is being displayed on screen in a ScrollView in SwiftUI. I understand that a ScrollView renders at one time rather than rendering as items appear (like in List), but I am constrained to using ScrollView as I have .scrollTo actions.
I also understand that in UIKit with UIScrollView, it is possible to use a CGRectIntersectsRect between the item frame and the ScrollView frame in the UIScrollViewDelegate but I would prefer to find a solution in SwiftUI if possible.
Example code looks like this:
ScrollView {
ScrollViewReader { action in
ZStack {
VStack {
ForEach(//array of chats) { chat in
//chat display bubble
.onAppear(perform: {chatsOnScreen.append(chat)})
}.onReceive(interactionHandler.$activeChat, perform: { _ in
//scroll to active chat
})
}
}
}
}
Ideally, when a user scrolls, it would check which items are on screen and zoom the view to fit the largest item on screen.
SwiftUI’s ScrollView allows us to create scrolling containers of views relatively easily, because it automatically sizes itself to fit the content we place inside it and also automatically adds extra insets to avoid the safe area. Scroll views are vertical by default, but you can control the axis by passing in .horizontal as the first parameter.
List view is a view that contains child views and displays them in a scrollable manner. SwiftUI offers two views with this capability, ScrollView and List. In the previous post, we learned two ways to populate a list view's content.
The usage of a scroll view is pretty simple. You create a scroll view and pass the content inside a ViewBuilder closure. Let’s take a look at the first example. We have a few parameters to control the scroll view behavior. We can set the axis of the scroll. It can be horizontal, vertical, or both.
As the user performs platform-appropriate scroll gestures, the scroll view adjusts what portion of the underlying content is visible. ScrollView can scroll horizontally, vertically, or both, but does not provide zooming functionality. In the following example, a ScrollView allows the user to scroll through a VStack containing 100 Text views.
When you use VStack
in ScrollView
all content is created at once at build time, so onAppear
does not fit your intention, instead you should use LazyVStack
, which will create each subview only before it appears on screen, so onAppear
will be called when you expect it.
So it should be like
ScrollViewReader { action in
ScrollView {
LazyVStack { // << this !!
ForEach(//array of chats) { chat in
//chat display bubble
.onAppear(perform: {chatsOnScreen.append(chat)})
}.onReceive(interactionHandler.$activeChat, perform: { _ in
//scroll to active chat
})
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With