Spatial SwiftUI: Using Look to Scroll

We can let users look to scroll with the Scroll Input Behavior modifier.

Overview

visionOS 26 includes a new feature called Look to Scroll. Users can look near the edge of a view to scroll its content. We can opt our views into this feature by using the new scrollInputBehavior.

ScrollView(.vertical, showsIndicators: false) {
...
}
.scrollInputBehavior(.enabled, for: .look)
  • behavior: automatic, enabled, disabled
  • input: .look or .look(axes:)

This feature is mostly useful on non-interactive content such as text and images. I would avoid using this on forms or complex interfaces.

Example Code

We have three examples:

  1. Vertical only
  2. Horizontal only
  3. Vertical and Horizontal with options to select active axes
struct Example100: View {

    @State private var inputKind: ScrollInputKind = .look

    var body: some View {
        TabView {

            // MARK: - Vertical list
            ScrollView(.vertical, showsIndicators: false) {
                VStack(alignment: .center, spacing: 16) {
                    ForEach(0..<101, id: \.self) { number in
                        Text("\(number)")
                            .font(.extraLargeTitle2)
                            .padding()
                            .frame(width: 120, height: 80)
                            .background(Color(.stepGreen))
                            .cornerRadius(16)
                    }
                }
                .padding()
            }
            .scrollInputBehavior(.enabled, for: .look)
            .tabItem {
                Label("Vertical", systemImage: "chevron.up.chevron.down")
            }

            // MARK: - Horizontal row
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .center, spacing: 16) {
                    ForEach(0..<101, id: \.self) { number in
                        Text("\(number)")
                            .font(.extraLargeTitle2)
                            .padding()
                            .frame(width: 120, height: 80)
                            .background(Color(.stepGreen))
                            .cornerRadius(16)
                    }
                }
                .padding()
            }
            .scrollInputBehavior(.enabled, for: .look)
            .tabItem {
                Label("Horizontal", systemImage: "chevron.left.chevron.right")
            }

            // MARK: - Scrollable grid (both axes)
            ScrollView([.horizontal, .vertical], showsIndicators: false) {
                let columns = Array(repeating: GridItem(.flexible(), spacing: 16), count: 12)

                LazyVGrid(columns: columns, spacing: 16) {
                    ForEach(0..<101, id: \.self) { number in
                        Text("\(number)")
                            .font(.extraLargeTitle2)
                            .padding()
                            .frame(width: 120, height: 80)
                            .background(Color(.stepGreen))
                            .cornerRadius(16)
                    }

                }
                .padding()
            }
            .background(.thickMaterial)
            .scrollInputBehavior(.enabled, for: inputKind)
            .ornament(attachmentAnchor: .scene(.top), contentAlignment: .bottom, ornament: {
                let display = inputKind == .look ? "All" : inputKind == .look(axes: .horizontal) ? "Horizontal" : "Vertical"
                Text("\(display)")

                .padding()
                .glassBackgroundEffect()
            })
            .ornament(attachmentAnchor: .scene(.trailing), contentAlignment: .leading, ornament: {
                VStack {
                    Button(action: {
                        inputKind = .look
                    }, label: {
                        Image(systemName: "arrow.up.and.down.and.arrow.left.and.right")
                    })
                    Button(action: {
                        inputKind = .look(axes: .vertical)
                    }, label: {
                        Image(systemName: "chevron.up.chevron.down")
                    })
                    Button(action: {
                        inputKind = .look(axes: .horizontal)
                    }, label: {
                        Image(systemName: "chevron.left.chevron.right")
                    })
                }

                .padding()
                .glassBackgroundEffect()
            })
            .tabItem {
                Label("Grid", systemImage: "square.grid.3x3")
            }
        }
    }
}

Support our work so we can continue to bring you new examples and articles.

Questions or feedback?