我正在开发一个 IOS SwiftUI 项目,我正在尝试复制这种滚动动画行为(不确定它是否有名称):
我想要什么: 当用户滚动时,多个视图出现、消失(带有淡入淡出、动画)、多个文本视图在顶部滚动等。该示例已在 CSS 中完成,我想在 SwiftUI 中完成。
我的第一次尝试是这样的:
Scrollview
Vstack
用于滚动内容(文本)
ScrollId
观察用户位置
.Id
针对不同内容手动设置
ZStack
为固定内容.allowsHitTesting(false)
如果以
ScrollId
为条件使固定内容出现和消失
(见下面的代码)
它可以工作,但很快就会变得混乱(手动 ID 归属、固定元素位于代码末尾等......)。这真的是最好的方法吗?
然后,我希望这些蓝色图表在滚动时具有动画效果(如示例中所示)。仅通过观察个体
ScrollID
的变化来做到这一点,如果条件看起来非常严重。
对蓝色图表进行动画处理:除了
ScrollID
更改之外,没有任何内容会触发蓝色图表的外观。我怎样才能使每个矩形逐渐按比例显示?或者动画他们的不透明度等?我需要为它们每个人创建一个变量吗?
不能使用 onappear 来制作任何动画:我在某处读到,在
scrollview
中,视图在出现在屏幕上之前加载,因此,并没有真正工作。事实上,它本来很方便,但没有达到预期的效果。
一些截图:
滚动内容
ScrollID达到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()
}
如果您希望更新更精细,那么我建议将其基于滚动偏移量。这可以通过在滚动内容的背景中使用
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()
}