考虑以下示例:
struct ContentView: View {
@State var showSplash: Bool = true
@Namespace var animationNamespace
var body: some View {
ZStack {
if showSplash {
GeometryReader { geometry in
AsyncImage(url: URL(string: "https://picsum.photos/seed/864a5875-6d8b-43d6-8d65-04c5cfb13f3b/1920/1440")) { image in
image.resizable()
.scaledToFill()
.matchedGeometryEffect(id: "SplashImage", in: animationNamespace)
.transition(.move(edge: .bottom))
.frame(width: geometry.size.width)
.transition(.move(edge: .bottom))
.edgesIgnoringSafeArea(.all)
.clipped()
} placeholder: {
Color.gray
}
}
.onTapGesture {
toggleSplashScreen(false)
}
} else {
ScrollView {
GeometryReader { geometry in
AsyncImage(url: URL(string: "https://picsum.photos/seed/864a5875-6d8b-43d6-8d65-04c5cfb13f3b/1920/1440")) { image in
image
image
.resizable()
.scaledToFill()
.matchedGeometryEffect(id: "SplashImage", in: animationNamespace)
.transition(.move(edge: .bottom))
} placeholder: {
Color.gray
}
.frame(width: geometry.size.width, height: 400)
.clipped()
}
.edgesIgnoringSafeArea(.all)
.onTapGesture {
toggleSplashScreen(true)
}
}
}
}
}
}
这里有一个辅助方法:
private extension ContentView {
func toggleSplashScreen(_ toggle: Bool) {
withAnimation(.spring(response: 0.85, dampingFraction: 0.95)) {
showSplash = toggle
}
}
}
这会产生:
我注意到这里有两件事我想修复
- 两种状态之间转换时闪烁的白色效果。
- 我注意到,由于我们使用的是
AsyncImage
,当showSplash
更改 AsyncImage 时,有时只会命中placeholder
block 。结果,过渡变得非常不稳定。我使用 Assets 文件中的静态图像对此进行了测试,然后过渡变得平滑。我还尝试在 AsyncImage 上创建缓存机制,但有时仍会遇到placeholder
block 的问题。
很想听听任何想法:)谢谢!
最佳答案
我认为您可以采取一些措施来改进这一点。
首先,您正在与 SwiftUI 维护 View 身份的方式进行一些斗争。 SwiftUI 确定何时可以重用现有结构而不是重新创建结构的方法之一是通过它在 View 层次结构中的位置。因此,当您切换结构时,您会从:
GeometryReader
AsyncImage
至
ScrollView
GeometryReader
AsyncImage
因此,系统认为这是两个 AsyncImage
View ,因此每次都会重建 View (并重新加载图像)。我认为这就是白色闪光的来源,因为您在动画中间看到灰色占位符。如果您可以保留 ScrollView ,并在不需要时禁用滚动(如果可能的话),那么操作系统可以维护 AsyncImage
的标识。 (参见https://developer.apple.com/videos/play/wwdc2021/10022/)
这就引出了您要调查的第二个领域。 AsyncImage
的美妙之处在于它为您从网络加载内容提供了便利。不幸的是,它并没有使通信速度更快。您的目标应该是让 AsyncImage
访问网络的次数尽可能少。
现在,您的调整大小策略侧重于调整图像大小。这意味着对于每次转换,您都在“访问网络”(请阅读将代码放在缓慢、尘土飞扬的土路上)。您应该只加载一次图像(缓慢的部分)并调整显示它的 View 的大小,而不是调整图像的大小。总体思路是让 AsyncImage
加载图像,然后通过对 View 框架进行动画处理来控制图像的动画效果。
这是我得到的帮助较少的地方。我对 AsyncImage 的了解不够,不知道它是否能够实现该策略。似乎应该是……但我不知道是不是。您可能必须下载图像并将其存储为与呈现图像的 View 分开的状态。
所以我的建议是限制 AsyncImage
重新加载网络数据的次数。这涉及帮助 SwiftUI 维护 AsyncImage 的标识,这样就不必在每次创建 View 时重新加载。并且,尝试在 View 而不是图像上实现动画和缩放,因为重新缩放图像还需要重新加载网络。
关于ios - 与 AsyncImage iOS 15 匹配的几何效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69673264/