Skip to content

ScrollView Scroll Visibility (SwiftUI)

A SwiftUI guide for coding agents. Also covers onscrollvisibilitychange, visible items in scroll view, autoplay video on scroll, onscrolltargetvisibilitychange, visibility threshold viewport.

Two iOS 18 modifiers report what is visible in a ScrollView: onScrollTargetVisibilityChange gives the scroll view the list of visible ids, and onScrollVisibilityChange tells one view about its own visibility. Both take a threshold for how much of the view counts as visible.

Visible Identifiers

Mark the lazy stack with scrollTargetLayout so the children, not the stack, are the targets. Then attach onScrollTargetVisibilityChange to the scroll view with the id type.

@State private var visible: [Int] = []

ScrollView {
  LazyVStack {
    ForEach(1..<100, id: \.self) { item in
      Text(verbatim: item.formatted())
    }
  }
  .scrollTargetLayout()
}
.onScrollTargetVisibilityChange(idType: Int.self) { identifiers in
  visible = identifiers
}

Per-View Visibility

onScrollVisibilityChange attaches to a view inside the scroll view and reports a boolean. The canonical use is media that plays only while on screen.

struct VideoPlayerView: View {
  let url: URL
  @State var player: AVPlayer?

  var body: some View {
    VideoPlayer(player: player)
      .task {
        if player == nil {
          player = AVPlayer(url: url)
        }
      }
      .onScrollVisibilityChange { isVisible in
        if isVisible {
          player?.play()
        } else {
          player?.pause()
        }
      }
  }
}

The Threshold

Both modifiers accept threshold, the fraction of the view that must be visible before the action fires. The default is 0.5. A threshold: 0.1 fires once 10 percent shows, and fires again when visibility drops back under it.

.onScrollVisibilityChange(threshold: 0.1) { isVisible in
  // ...
}

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-mastering-scrollview-scroll-visibility" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems