LoginSignup
3
2

Scroll to Bottomを簡潔に実装する

Posted at

結論

import SwiftUI

struct ContentView: View {
    @State private var scrollTrigger = ChangeTransmitter()
    var body: some View {
        VStack {
            MyScrollView(scrollTrigger: $scrollTrigger)
            Button("scroll to bottom") {
                scrollTrigger.trigger()
            }
        }
    }
}

struct MyScrollView: View {
    @Binding var scrollTrigger: ChangeTransmitter
    private let scrollTarget = "bottomID"

    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                ForEach(0..<100) { index in
                    Text("\(index)")
                }
                Color.clear.frame(height: 1)
                    .id(scrollTarget)
            }
            .onChange(of: scrollTrigger) { _, _ in
                withAnimation {
                    proxy.scrollTo(scrollTarget)
                }
            }
        }
    }
}

struct ChangeTransmitter: Equatable {
    private var changeCount = 0
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.changeCount == rhs.changeCount
    }
    mutating func trigger() {
        self.changeCount += 1
    }
}

#Preview {
    ContentView()
}

RocketSim_Recording_iPhone_15_6.1_2024-05-05_19.02.31.gif

経緯

このように書いても…
import SwiftUI

struct ContentView: View {
    @State private var scrollTarget: Int?
    var body: some View {
        VStack {
            MyScrollView(scrollTarget: $scrollTarget)
            Button("scroll") {
                scrollTarget = 1
            }
        }
    }
}

struct MyScrollView: View {
    @Binding var scrollTarget: Int?
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                ForEach(0..<100) { index in
                    Text("\(index)")
                }
                Color.clear.frame(height: 1)
                    .id(1)
            }
            .onChange(of: scrollTarget) { _, newTarget in
                withAnimation {
                    proxy.scrollTo(newTarget)
                }
            }
        }
    }
}

#Preview {
    ContentView()
}

一度Scroll to Bottomした後、手動で位置を変え再度Scroll to Bottomを試みても、二回目以降は onChanged{} が呼ばれないためできない。

RocketSim_Recording_iPhone_15_6.1_2024-05-05_18.33.26.gif

以下のように書いても、変化が早すぎるためかScrollされない。

...
Button("scroll") {
    scrollTarget = nil
    scrollTarget = 1
}
...

また、Scroll to Bottomを実現したいだけならscrollTargetMyScrollView内部に隠蔽してしまいたい。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2