📜
SwiftUIで左右の無限スクロール
iOS17から追加される
defaultScrollAnchor()
でScrollViewの開始位置を真ん中にした上で、以下の
handleItemAppearance(item: item)
のように、ScrollViewの端っこに到達したときに、itemsに要素を追加すると、左右の無限スクロールができます。
import SwiftUI
struct InfiniteScrollView: View {
@State private var items: [Int] = Array(0..<10) // Initial data
@State private var isLoading = false
var body: some View {
ScrollView(.horizontal) {
LazyHStack{
ForEach(items, id: \.self) { item in
RoundedRectangle(cornerRadius: 10)
.fill(.orange)
.frame(width: 150, height: 40)
.overlay {
Text("Item \(item)")
}
.containerRelativeFrame(.horizontal, count: 8, span: 3, spacing: 0)
.onAppear{
handleItemAppearance(item: item)
}
}
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.viewAligned)
.defaultScrollAnchor(.center)
}
private func handleItemAppearance(item: Int) {
if item == items.last {
updateIntList(byAdding: 1)
} else if item == items.first {
updateIntList(byAdding: -1)
}
}
private func updateIntList(byAdding valueToAdd: Int) {
guard !isLoading else { return }
isLoading = true
if let referenceValue = valueToAdd > 0 ? items.last : items.first {
let newValue = referenceValue + valueToAdd
if valueToAdd > 0 {
items.append(newValue)
} else {
items.insert(newValue, at: 0)
}
}
isLoading = false
}
}
#Preview {
InfiniteScrollView()
}
ScrollViewの開始位置が端っこだと、起動した途端に要素が無限に増え初めますが、開始位置を真ん中にすれば回避できます。
左右にスクロールするカレンダーアプリなら、以下のように現在時刻を起点に前後10日を取得し、それをForEachで回して.defaultScrollAnchor(.center)
にすれば、現在日を真ん中に表示しながら無限スクロールが実装できます
.onAppear {
// 今日を中心に前後10日(合計20日)の日付リストを作成
for i in -10...10 {
if let newDate = Calendar.current.date(byAdding: .day, value: i, to: currentDate) {
dateList.append(newDate)
}
}
}
ただ派手にスクロールすると挙動が不安定ですが...
Discussion