创建 Elastic Header 动画效果(Apple TV)
了解如何创建类似 Apple TV 应用中的视差头部效果(Parallax Header)/弹性头部效果(Elastic Header)。

在 Apple TV 移动应用中,随着用户滑动,顶部图片会以不同速度移动产生深度感和拉伸感。
0:00
/0:04
苹果 TV 应用中的设计效果
这种效果在 UI 设计中,通常被称为"视差头部效果"(Parallax Header Effect)或"弹性头部效果"(Elastic Header Effect)。
- 图片顶部始终固定在屏幕顶端
- 随着手指往下滑动,图片在纵轴和横轴上均会被轻微放大。
GeometryReader + Frame
创建 Elastic Header Effect 的常见方式是使用 GeometryReader
结合 .frame
修饰器:
- 通过
GeometryReader
获取用户滑动时的偏移量。 - 通过偏移量,动态计算
.frame()
的高度和宽度,从而实现图片放大效果。 - 还可以通过偏移量,动态计算
.offset
,实现图片拉伸效果。
示例代码结构是这样的:
var body: some View {
GeometryReader { proxy in
let offset = offset(for: proxy)
let heightModifier = heightModifier(for: proxy)
let blurRadius = min(
heightModifier / 20,
max(10, heightModifier / 20)
)
content()
.edgesIgnoringSafeArea(.horizontal)
.frame(
width: proxy.size.width,
height: proxy.size.height + heightModifier
)
.offset(y: offset)
.blur(radius: blurRadius)
}.frame(height: defaultHeight)
}
在 iOS18 上(WWDC24),苹果为 ScrollView 添加了众多功能。
通过结合使用 ScrollGeometry
和 .onScrollPhaseChange()
修饰器,现在可以更加简单的实现 Elastic Header Effect 效果。
ScrollGeometry | Apple Developer Documentation
A type that defines the geometry of a scroll view.

onScrollPhaseChange(_:) | Apple Developer Documentation
Adds an action to perform when the scroll phase of the first scroll view in the hierarchy changes.

ScrollGeometry + onScrollPhaseChange
创建基本布局
创建基本的布局效果,有几个关键点:
- 使用
ScrollView()
添加滑动功能,使用.scrollIndicators(.hidden)
隐藏滚动条。 - 使用
ZStack
创建布局,封面图片作为下层,其他文字部分作为上层展示。不使用.overlay()
是为了添加上滑时滚动视差效果。 - 使用 .ignoresSafeArea(.container, edges: .top),让图片对齐屏幕顶部区域,忽略安全区。
基础布局代码结构如下:
ScrollView {
VStack(alignment: .center, spacing: 16) {
ZStack(alignment: .bottomLeading) {
// 封面图片
Image(bookmark.coverImage)
.padding(.horizontal, -16)
MovieInfoView(bookmark: bookmark)
}
}
.frame(maxWidth: .infinity)
.padding(.horizontal, 16)
}
.ignoresSafeArea(.container, edges: .top)
.scrollIndicators(.hidden)
这将能够创建类似下面这样的效果:
0:00
/0:04
添加 ScrollGeometry
变量
首先,我们需要创建一个 ScrollGeometry
对象,通过它来获取滚动数据(内容偏移量、滚动边界、滚动内容大小等),这是所有后续添加动画效果的基础。
// Scroll Animation Properties
@State private var scrollProperties: ScrollGeometry = .init(
contentOffset: .zero,
contentSize: .zero,
contentInsets: .init(),
containerSize: .zero
)
创建一个 scrollProperties 变量,它是一个 ScrollGeometry 对象。