While there are the same sort of built in functions in UIView, as you are showing, this isn't built in to SwiftUI yet, but you can do it yourself. Essentially what you do is get the coordinates of the outer view, in this case your ScrollView
, and see if the inner coordinates of you image view are at least partially within the outer view. This is done with two GeometryReaders
and a .coordinateSpaces()
designator so we can reliably be in the same coordinate space. If the image exists within the outer coordinate space, then it is added to a @State
variable which is a set. if not, it is removed from the set. This gives you are running list of what views are currently partially or fully visible.
Since you did not give a Minimal, Reproducible Example, I took your code and used Rectangles
to represent your images, but you should be able to simply reinsert your code. Below is a working example:
struct ImageIsInView: View {
@State var visibleIndex: Set<Int> = [0,1]
var body: some View {
VStack {
Text(visibleIndex.map( { $0.description }).sorted().joined(separator: ", "))
// The outer GeometryReader has to go directly around your ScrollView
GeometryReader { outerProxy in
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .top) {
ForEach(0..<10, id: \.self) { item in
GeometryReader { geometry in
Rectangle()
.fill(Color.orange)
.cornerRadius(13)
.overlay(
Text("Item: \(item)")
)
// every time the ScrollView moves, the inner geometry changes and is
// picked up here:
.onChange(of: geometry.frame(in: .named("scrollView"))) { imageRect in
if isInView(innerRect: imageRect, isIn: outerProxy) {
visibleIndex.insert(item)
} else {
visibleIndex.remove(item)
}
}
}
.frame(width: 200, height: 200)
}
}
}
.coordinateSpace(name: "scrollView")
}
}
}
private func isInView(innerRect:CGRect, isIn outerProxy:GeometryProxy) -> Bool {
let innerOrigin = innerRect.origin.x
let imageWidth = innerRect.width
let scrollOrigin = outerProxy.frame(in: .global).origin.x
let scrollWidth = outerProxy.size.width
if innerOrigin + imageWidth < scrollOrigin + scrollWidth && innerOrigin + imageWidth > scrollOrigin ||
innerOrigin + imageWidth > scrollOrigin && innerOrigin < scrollOrigin + scrollWidth {
return true
}
return false
}
}