【SwiftUI】ScrollView内のコンテンツがスクロールを必要とするまでスクロールを制限する（iOS16.4）

# はじめに
iOS16.4から`scrollBounceBehavior`が使用できるようになりました。
これはScrollViewだけでなく、Listにも使用できるようです。

# サンプルアプリ
ScrollView内のコンテンツが少ない時はスクロールできません。
![Simulator Screen Recording - iPhone 15 Pro - 2024-02-14 at 22.33.47.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/9741b78c-41d8-4a2a-311e-f994bde21b39.gif)

# 実装
```swift
import SwiftUI

struct ContentView: View {
    struct Item {
        let id = UUID()
        let color: Color
    }
    
    @State private var items = [Item]()

    var body: some View {
        ScrollView {
            VStack {
                Button {
                    addItem()
                } label: {
                    Text("要素追加")
                }
                .buttonStyle(.borderedProminent)
                .frame(maxWidth: .infinity)

                ForEach(items, id: \.id) { item in
                    Text(item.id.uuidString)
                        .frame(maxWidth: .infinity)
                        .frame(height: 60)
                        .background(item.color)
                }
            }
        }
        .scrollBounceBehavior(.basedOnSize)
    }
    
    private func addItem() {
        items.append(.init(color: .init(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1))))
    }
}
```

# ドキュメントコメント
### scrollBounceBehavior
```swift
/// Configures the bounce behavior of scrollable views along the specified
/// axis.
///
/// Use this modifier to indicate whether scrollable views bounce when
/// people scroll to the end of the view's content, taking into account the
/// relative sizes of the view and its content. For example, the following
/// ``ScrollView`` only enables bounce behavior if its content is large
/// enough to require scrolling:
///
///     ScrollView {
///         Text("Small")
///         Text("Content")
///     }
///     .scrollBounceBehavior(.basedOnSize)
///
/// The modifier passes the scroll bounce mode through the ``Environment``,
/// which means that the mode affects any scrollable views in the modified
/// view hierarchy. Provide an axis to the modifier to constrain the kinds
/// of scrollable views that the mode affects. For example, all the scroll
/// views in the following example can access the mode value, but
/// only the two nested scroll views are affected, because only they use
/// horizontal scrolling:
///
///     ScrollView { // Defaults to vertical scrolling.
///         ScrollView(.horizontal) {
///             ShelfContent()
///         }
///         ScrollView(.horizontal) {
///             ShelfContent()
///         }
///     }
///     .scrollBounceBehavior(.basedOnSize, axes: .horizontal)
///
/// You can use this modifier to configure any kind of scrollable view,
/// including ``ScrollView``, ``List``, ``Table``, and ``TextEditor``:
///
///     List {
///         Text("Hello")
///         Text("World")
///     }
///     .scrollBounceBehavior(.basedOnSize)
///
/// - Parameters:
///   - behavior: The bounce behavior to apply to any scrollable views
///     within the configured view. Use one of the ``ScrollBounceBehavior``
///     values.
///   - axes: The set of axes to apply `behavior` to. The default is
///     ``Axis/vertical``.
///
/// - Returns: A view that's configured with the specified scroll bounce
///   behavior.
@available(iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4, *)
public func scrollBounceBehavior(_ behavior: ScrollBounceBehavior, axes: Axis.Set = [.vertical]) -> some View
```

### basedOnSize
```swift
/// The scrollable view bounces when its content is large enough to require
/// scrolling.
///
/// The scrollable view bounces along the specified axis if the size of
/// the content exceeeds the size of the scrollable view in that axis.
public static var basedOnSize: ScrollBounceBehavior { get }
```

# おわり
iOS16.4より前に同じことをやろうとすると`GeometryReader`が登場してきてちょっとめんどそうですね

# 公式ドキュメント
https://developer.apple.com/documentation/swiftui/scrollbouncebehavior

https://developer.apple.com/documentation/swiftui/view/scrollbouncebehavior(_:axes:)

https://developer.apple.com/documentation/swiftui/scrollbouncebehavior/basedonsize?changes=latest_major