SwiftUI 6.0(iOS 18)ScrollView 全新的滚动位置(ScrollPosition)揭秘

06-25 608阅读

SwiftUI 6.0(iOS 18)ScrollView 全新的滚动位置(ScrollPosition)揭秘

概览

在只有方寸之间大小的手持设备上要想体面的向用户展示海量信息,滚动视图(ScrollView)无疑是绝佳的“东牀之选”。

SwiftUI 6.0(iOS 18)ScrollView 全新的滚动位置(ScrollPosition)揭秘

在 SwiftUI 历史的长河中,总觉得苹果对于 ScrollView 视图功能的升级是在“挤牙膏”。这不,在本届最新 WWDC24 重磅打造的 SwiftUI 6.0 中就让我们来看看 ScrollView 又能挤出怎样的新花样吧?

在本篇博文中,您将学到如下精彩的内容:

  • 概览
  • 1. SwiftUI 6.0 之前的滚动世界
  • 2. SwiftUI 6.0(iOS 18)中全新的 ScrollPosition 类型
  • 3. “新老搭配,干活不累”
  • 4. 如何判断当前滚动是由用户指尖触发的?
  • 5. 实时监听滚动视图的内容偏移(ContentOffset)
  • 总结

    在 WWDC24 里,苹果对 SwiftUI 6.0 中滚动视图的全新升级无疑解了一众秃头码农们的额燃眉之急。

    那还等什么呢?让我们马上开始滚动大冒险吧!

    Let‘s rolling!!!😉


    1. SwiftUI 6.0 之前的滚动世界

    苹果从 SwiftUI 2.0 开始陆续“发力”向 ScrollView 增加了许多新特性,其中包括秃头码农们翘首跂踵的滚动位置读取与设置、滚动模式等高级功能。

    在 SwiftUI 6.0 之前,我们是通过单一状态来读取和设置滚动位置的:

    struct ContentView: View {
        @State private var position: Int?
        
        var body: some View {
            ScrollView {
                LazyVStack {
                    ForEach(0.. index in
                        Text(verbatim: index.formatted())
                            .id(index)
                    }
                }
                .scrollTargetLayout()
            }
            .scrollTargetBehavior(.viewAligned)
            .scrollPosition(id: $position)
        }
    }
    
        @State private var position = ScrollPosition(edge: .top)
        
        var body: some View {
            ScrollView {
                Button("Scroll to bottom") {
                    position.scrollTo(edge: .bottom)
                }
                
                ForEach(1.. index in
                    Text(verbatim: index.formatted())
                        .id(index)
                }
                
                Button("Scroll to top") {
                    position.scrollTo(edge: .top)
                }
            }
            .scrollPosition($position)
        }
    }
    
        @State private var position = ScrollPosition(edge: .top)
        
        var body: some View {
            ScrollView {
                Button("Random Scroll") {
                    let id = (1.. index in
                    Text(verbatim: index.formatted())
                        .id(index)
                }
            }
            .scrollPosition($position)
            .animation(.default, value: position)
        }
    }
    
        @State private var position = ScrollPosition(edge: .top)
        
        var body: some View {
            ScrollView {
                Button("Scroll to offset") {
                    position.scrollTo(point: CGPoint(x: 0, y: 100))
                }
                
                ForEach(1.. index in
                    Text(verbatim: index.formatted())
                        .id(index)
                }
            }
            .scrollPosition($position)
            .animation(.default, value: position)
        }
    }
    
        position.scrollTo(y: 100)
        position.scrollTo(x: 200)
    }
    
        @State private var position = ScrollPosition(edge: .top)
        @State var curPosID: Int?
        @State var offsetY: CGFloat?
        
        var body: some View {
            ScrollView {
                ForEach(1.. index in
                    Text(verbatim: index.formatted())
                        .font(.largeTitle.weight(.heavy))
                        .padding()
                        .id(index)
                }
                .scrollTargetLayout()
            }
            .scrollPosition(id: $curPosID)
            .scrollPosition($position)
            .animation(.default, value: position)
            .safeAreaInset(edge: .bottom) {
                Button("Random Scroll") {
                    let id = (1.. old,new in
                print("用代码滚动视图的ID: \(new.viewID)")
                curPosID = new.viewID as? Int
            }
            .onChange(of: curPosID) { _,new in
                print("实时滚动视图的 ID: \(new)")
            }
        }
    }
    
        @State private var position = ScrollPosition(edge: .top)
        
        var body: some View {
            ScrollView {
                ForEach(1.. index in
                    Text(verbatim: index.formatted())
                        .font(.largeTitle.weight(.heavy))
                        .padding()
                        .id(index)
                }
            }
            .scrollPosition($position)
            .animation(.default, value: position)
            .safeAreaInset(edge: .bottom) {
                Button("Random Scroll") {
                    let id = (1.. old,new in
                print("是否由用户拖动引起的滚动:\(new.isPositionedByUser ? "是" : "否")")
            }
        }
    }
     old,new in
        print("当前内容滚动偏移:\(new.point)")
    }
    
        @State private var position = ScrollPosition(edge: .top)
        @State var curPosID: Int?
        @State var offsetY: CGFloat?
        
        var body: some View {
            ScrollView {
                
                ForEach(1.. index in
                    Text(verbatim: index.formatted())
                        .font(.largeTitle.weight(.heavy))
                        .padding()
                        .id(index)
                }
                .scrollTargetLayout()
            }
            .scrollPosition(id: $curPosID)
            .scrollPosition($position)
            .animation(.default, value: position)
            .safeAreaInset(edge: .bottom) {
                Button("Random Scroll") {
                    let id = (1.. old,new in
                print("用代码滚动视图的ID: \(new.viewID)")
                curPosID = new.viewID as? Int
            }
            .onChange(of: curPosID) { _,new in
                print("实时滚动视图的 ID: \(new)")
            }
            .onScrollGeometryChange(for: CGFloat.self, of: {
                geo in
                geo.contentOffset.y
            }, action: { old, new in
                offsetY = new
            })
            .onChange(of: offsetY) { _, new in
                guard let new else { return }
                print("当前 y 轴滚动偏移:\(new.formatted())")
            }
        }
    }
    
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]