TechnologiesSwiftUIScrolling

ScrollTargetBehaviorProperties struct

iOSmacOStvOSwatchOSvisionOSiOS 18.4+✓ renders

Properties influencing the scroll view a scroll target behavior

How it works

ScrollTargetBehaviorProperties is the structure that SwiftUI hands to a scroll target behavior so it can declare how it wants to participate in scrolling. Rather than configuring a behavior with scattered modifiers, the properties value carries a behavior's preferences — such as whether the behavior intends to take over scroll deceleration — in one place that the scroll system reads when it composes its final motion. Reach for it when you write a custom type conforming to ScrollTargetBehavior and need to advertise that behavior's characteristics to the surrounding scroll view.

  1. Conform a type to ScrollTargetBehavior

    ScrollTargetBehaviorProperties exists to describe a behavior, so you first need a behavior to describe. A custom type that adopts the ScrollTargetBehavior protocol becomes the unit the scroll view consults, like SnapBehavior in the example, which conforms to ScrollTargetBehavior.

  2. Resolve each target in updateTarget(_:context:)

    The protocol's core requirement is where the behavior does its work, computing where scrolling should come to rest. Here updateTarget(_ target: inout ScrollTarget, context: TargetContext) reads target.rect.origin.y and rewrites it, snapping the resting offset to the nearest multiple of height.

  3. Expose preferences through ScrollTargetBehaviorProperties

    Alongside updateTarget, a behavior can surface a ScrollTargetBehaviorProperties value to tell the scroll system how it behaves at a higher level — for instance, declaring its relationship to the scroll view's deceleration. SnapBehavior relies on the default properties, which is why it only needs to override the resolution logic in updateTarget.

  4. Attach the behavior with scrollTargetBehavior(_:)

    A behavior, and the properties it reports, only take effect once installed on a scroll container. The .scrollTargetBehavior(SnapBehavior()) modifier on the ScrollView is what wires the behavior — and thus its ScrollTargetBehaviorProperties — into the live scroll system.

Try it — Change let height: CGFloat = 60 in updateTarget to a larger value like 120 and watch each gesture settle on coarser, more widely spaced resting positions.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

ScrollTargetBehaviorProperties.swift
struct ScrollTargetBehaviorPropertiesDemo: View {
    var body: some View {
        ScrollView {
            VStack(spacing: 16) {
                ForEach(1..<13) { i in
                    Text("Item \(i)")
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(Color.blue.opacity(0.15))
                        .cornerRadius(8)
                }
            }
            .padding()
        }
        .scrollTargetBehavior(SnapBehavior())
    }
}

struct SnapBehavior: ScrollTargetBehavior {
    func updateTarget(_ target: inout ScrollTarget, context: TargetContext) {
        let height: CGFloat = 60
        target.rect.origin.y = (target.rect.origin.y / height).rounded() * height
    }
}
Live preview
Item 1 Item 2 Item 3 Item 4 Item 5 Item 6 Item 7 Item 8 Item 9 Item 10 Item 11 Item 12
Item 1 Item 2 Item 3 Item 4 Item 5 Item 6 Item 7 Item 8 Item 9 Item 10 Item 11 Item 12
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →