【问题标题】:Matched Geometry Effect with AsyncImage iOS 15使用 AsyncImage iOS 15 匹配几何效果
【发布时间】:2021-12-08 21:14:14
【问题描述】:

考虑以下示例:

    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
        }
    }
}

这会产生:

我注意到这里有两件事我想解决

  1. 在两种状态之间转换时闪烁的白色效果。
  2. 我注意到,由于我们使用的是AsyncImage,当showSplash 更改时,AsyncImages 有时只会碰到placeholder 块。结果,过渡变得非常不稳定。我使用资产文件中的静态图像对此进行了测试,然后过渡变得平滑。我还尝试在 AsyncImage 上创建缓存机制,但有时会遇到placeholder 块的问题。

很想听听任何想法 :) 谢谢!

【问题讨论】:

  • MatchedGeometry 效果比您所拥有的要复杂一些。 SwiftUI Lab 有很多关于这个和高级动画的帖子。我认为您遇到的问题是您没有向MatchedGeometry 提供在两个视图之间无缝转换所需的信息,因此您得到了您所看到的行为。 MatchedGeometry 需要一种方法来计算转换之间的样子,而这在您的代码中不存在。

标签: ios swiftui ios15 swiftui-geometryeffect swiftui-asyncimage


【解决方案1】:

我认为您可以采取一些措施来改善这一点。

首先,您有点反对 SwiftUI 维护视图身份的方式。 SwiftUI 确定何时可以重用现有结构而不是重新创建结构的方法之一是通过它在视图层次结构中的位置。因此,当您切换结构时,您将从:

GeometryReader 
  AsyncImage

ScrollView
  GeometryReader
    AsyncImage

因此,系统认为这是两个AsyncImage 视图,因此它每次都重建视图(并重新加载图像)。我认为这就是您的白色闪光的来源,因为您在动画中间看到了灰色占位符。如果您可以保留滚动视图,可能在不需要时禁用滚动(如果可能的话),那么操作系统可以维护AsyncImage 的身份。 (见https://developer.apple.com/videos/play/wwdc2021/10022/

这就引出了你要研究的第二个领域。 AsyncImage 非常方便,它为您从网络加载内容提供了便利。不幸的是,它并没有使这种通信更快。您的目标应该是让AsyncImage 尽可能少地访问网络。

目前,您的调整大小策略侧重于调整图像大小。这意味着对于每次转换,您都在“访问网络”(阅读将您的代码放在缓慢、尘土飞扬、泥泞的道路上)。而不是调整图像的大小,您应该只加载一次图像(慢速部分)并调整显示它的视图的大小。一般的想法是让AsyncImage 加载图像,然后通过动画视图的框架来控制图像的动画效果。

这是我不太乐于助人的地方。我对AsyncImage 的了解还不够,不知道它是否能够实施该策略。似乎应该是……但我不知道是不是。您可能不得不求助于下载图像并将其存储为与呈现它的视图分开的状态。

所以我的建议是限制AsyncImage 必须重新加载网络数据的次数。这涉及帮助 SwiftUI 维护 AsyncImage 的身份,因此它不必在每次创建视图时重新加载。并且,尝试在视图而不是图像上实现动画和缩放,因为重新缩放图像还需要重新加载网络。

【讨论】:

    猜你喜欢
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多