SwiftUI / ScrollView中基于scrollPosition的动画

问题描述 投票:0回答:1

我正在开发一个 IOS SwiftUI 项目,我正在尝试复制这种滚动动画行为(不确定它是否有名称):

https://www.lemonde.fr/les-decodeurs/visuel/2023/06/07/comprendre-le-rechauffement-comment-nous-avons-bouleverse-le-climat_6176490_4355770.html

我想要什么: 当用户滚动时,多个视图出现、消失(带有淡入淡出、动画)、多个文本视图在顶部滚动等。该示例已在 CSS 中完成,我想在 SwiftUI 中完成。

我的第一次尝试是这样的:

  • Scrollview

  • Vstack
    用于滚动内容(文本)

  • ScrollId
    观察用户位置

  • .Id
    针对不同内容手动设置

  • ZStack
    为固定内容
    .allowsHitTesting(false)

  • 如果以

    ScrollId
    为条件使固定内容出现和消失

(见下面的代码)

它可以工作,但很快就会变得混乱(手动 ID 归属、固定元素位于代码末尾等......)。这真的是最好的方法吗?

然后,我希望这些蓝色图表在滚动时具有动画效果(如示例中所示)。仅通过观察个体

ScrollID
的变化来做到这一点,如果条件看起来非常严重。

  • 对蓝色图表进行动画处理:除了

    ScrollID
    更改之外,没有任何内容会触发蓝色图表的外观。我怎样才能使每个矩形逐渐按比例显示?或者动画他们的不透明度等?我需要为它们每个人创建一个变量吗?

  • 不能使用 onappear 来制作任何动画:我在某处读到,在

    scrollview
    中,视图在出现在屏幕上之前加载,因此,并没有真正工作。事实上,它本来很方便,但没有达到预期的效果。

一些截图:

滚动内容

The scroll content

ScrollID达到7时出现的固定内容

The fixed content that appears when ScrollID reaches 7

非常感谢您的帮助


import SwiftUI

struct ContentView: View {
    @State private var scrollID: Int?
    
    var body: some View {
        ZStack {
            Color.black
            
            ScrollView (.vertical) {
                VStack {
                    //   SCROLL CONTENT HERE
                    Spacer()
                        .frame(height: 200)
                        .id(1)
                    
                    Text("Curabitur in facilisis ex. Fusce eget molestie ante, eget varius arcu. ")
                        .font(.caption)
                        .frame(width: 200)
                        .padding(20)
                        .background(Color.white)
                        .id(2)
                    
                    Spacer()
                        .frame(height: 200)
                        .id(3)
                    
                    Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque dictum ligula non arcu iaculis bibendum. Curabitur in facilisis ex. Fusce eget molestie ante, eget varius arcu. ")
                        .font(.caption)
                        .frame(width: 200)
                        .padding(20)
                        .background(Color.white)
                        .id(4)
                    
                    Spacer()
                        .frame(height: 200)
                        .id(5)
                    
                    Rectangle()
                        .fill(Color.gray)
                        .frame(width:300, height: 300)
                        .id(6)
                    
                    // Break in the content to display the fixed element (blue rectangle)
                    
                    Spacer()
                        .frame(height: 1200)
                        .id(7)
                    
                    // end of the break
                    
                    Text("Pellentesque dictum ligula non arcu iaculis bibendum. Curabitur in facilisis ex. Fusce eget molestie ante, eget varius arcu. ")
                        .font(.caption)
                        .frame(width: 200)
                        .padding(20)
                        .background(Color.white)
                        .id(8)
                    
                    
                    Spacer()
                        .frame(height: 200)
                        .id(9)
                    
                    Rectangle()
                        .fill(Color.gray)
                        .frame(width:300, height: 300)
                        .id(10)
                    
                }
                .ignoresSafeArea()
                .scrollTargetLayout()
                
            }
            .scrollPosition(id: $scrollID)
            .onChange(of: scrollID) {
                oldValue, newValue in
                print(newValue ?? "")}
            
            // FIXED CONTENT HERE (blue chart)
            Group {
                if scrollID == 7 {
                    HStack{
                        ForEach(0..<10) { index in
                            Rectangle()
                                .fill(Color.blue)
                                .frame(width: 20, height: 200)
                        }
                    }
                }
            }
//            To prevent scroll to be stuck
            .allowsHitTesting(false)
            
        }
        .ignoresSafeArea()
        
    }
}

#Preview {
    ContentView()
}

animation swiftui uiscrollview onchange scroll-position
1个回答
0
投票

如果您希望更新更精细,那么我建议将其基于滚动偏移量。这可以通过在滚动内容的背景中使用

GeometryReader
来找到。

外层的

GeometryReader

 可以测量屏幕高度。使用它,可以计算滚动分数。

如果愿意,您可以保留 ids 和

scrollID

 以便进行编程滚动,但图形效果不再需要它们。

@State private var scrollID: Int? @State private var scrolledFraction = CGFloat.zero private func scrollDetector(screenHeight: CGFloat) -> some View { GeometryReader { proxy in let minY = proxy.frame(in: .scrollView).minY Color.clear .onChange(of: minY) { oldVal, newVal in scrolledFraction = -minY / (proxy.size.height - screenHeight) } } } var body: some View { GeometryReader { outer in ZStack { Color.black ScrollView(.vertical) { VStack { // content as before } .ignoresSafeArea() .background(scrollDetector(screenHeight: outer.size.height)) .scrollTargetLayout() } .scrollPosition(id: $scrollID) // FIXED CONTENT HERE (blue chart) HStack{ ForEach(0..<Int(10.0 * scrolledFraction), id: \.self) { index in Rectangle() .fill(Color.blue) .frame(width: 20, height: 200) } Spacer() } .padding(50) // To prevent scroll to be stuck .allowsHitTesting(false) } } .ignoresSafeArea() }

Animation

© www.soinside.com 2019 - 2024. All rights reserved.