I have a section near the bottom of a ScrollView / LazyVStack. I want to show a popoverTip anchored to a button in that section, but only once the user has actually scrolled to it not on view load (otherwise the tip shows floating at the bottom of the screen).
I tried to use onAppear but it fires as soon as the view is inserted into the hierarchy which on a LazyVStack can happen slightly before the view is truly in the viewport.
I fell back to using onScrollVisibilityChange to write the @Parameter only when the section is actually visible:
.onScrollVisibilityChange { isVisible in
MyTip.sectionVisible = isVisible
}
This correctly gates visibility, but on some older devices I noticed that writing/reading @Parameter mid-scroll causes hitches.
Is there a recommended TipKit pattern for this use case: showing a popoverTip only when its anchor has been scrolled into view that doesn't require a reactive write during active scroll?
thanks
For a LazyVStack specifically, I'd strongly recommend using an inline TipView if possible. That would avoid the issue of having to track the identities of the visible subviews of your ScrollView.
There is the anchorID API that should allow you to have the inline tip arrow point directly to your button:
ScrollView {
LazyVStack {
⋯
Button("My Feature") {}
.tipAnchor("myFeatureTipAnchor")
TipView(myFeatureTip, arrowEdge: .top, anchorID: "myFeatureTipAnchor")
}
}
This also prevents having a tip appear while someone is scrolling.
If your tip only works as a popoverTip (understandable for some UIs), I'd look at using onScrollVisibilityChange in conjunction with the isPresented argument of popoverTip or creating a small wrapper to only apply the popoverTip modifier when the state value passed into is true:
@State
var showTip: Bool = false
ScrollView {
LazyVStack {
⋯
Button("My Feature") {}
.onScrollVisibilityChange { isVisible in showTip = isVisible }
.popoverTip(myFeatureTip, isPresented: $showTip) // Option A
.conditionalPopoverTip(showTip: showTip, tip: myFeatureTip) // Option B
}
}
extension View {
@ViewBuilder
func conditionalPopoverTip(showTip: Bool, tip: some Tip) -> some View {
switch showTip {
case true:
self.popoverTip(tip)
case false:
self
}
}
}
Alternatively you could have a little lightbulb button 💡 displayed next to the view and have the popover tip displayed when a user taps the button. We really recommend not having a popover displayed while content is being scrolled since it can interfere with user interaction. Look at setting tipBackgroundInteraction to .enabled if you do need to use a popover.
Thank you! I hope this helps!