SwiftUImatchedGeometryEffect 仅以一种方式设置动画
Posted
技术标签:
【中文标题】SwiftUImatchedGeometryEffect 仅以一种方式设置动画【英文标题】:SwiftUI matchedGeometryEffect only animates one way 【发布时间】:2021-03-23 01:35:22 【问题描述】:我有一个用于文件浏览器网格的 ZStack,当您导航到文件夹时它会添加图层。文件夹图标显示文件夹内容的预览。我在预览项目上有一个matchedGeometryEffect
,以便它们可以动画到打开的文件夹中。问题是动画只在关闭文件夹时发生,而不是在打开时发生。
(我知道这段代码还不能推广到包含更多内容的文件夹。如果我能让这些动画正常工作,我会做到这一点。
很抱歉一次转储大量代码,但一切都应该在这里,以便它可以在项目中试用。
struct File: Hashable
var symbol: String?
var name: String
var contents: [File]? = nil
struct GridNavNew: View
@Namespace private var ns
static var files: [File] = [
File(name: "Folder", contents: [
File(symbol: "cloud", name: "Cloudy"),
File(symbol: "cloud.hail", name: "Hail"),
File(symbol: "cloud.snow", name: "Snow"),
File(symbol: "cloud.fog", name: "Fog")
]),
File(symbol: "cube", name: "Cube"),
File(symbol: "books.vertical", name: "Books")
]
static let root = File(symbol: nil, name: "/", contents: files)
@State private var path: [File] = [root]
var body: some View
ZStack
// This exists because otherwise the transition doesn't play on the way out
// See https://sarunw.com/posts/how-to-fix-zstack-transition-animation-in-swiftui/
Text("A")
.opacity(0)
.zIndex(1)
ForEach(Array(path.enumerated()), id: \.offset) index, dir in
NavigationView
if let content = dir.contents
ScrollView
LazyVGrid(columns: [GridItem(), GridItem()])
ForEach(content, id: \.self) file in
Button
withAnimation
path.append(file)
label:
FileCell(file: file,
onTop: path.last == dir,
ns: ns)
// .animation(.default)
.padding()
.navigationTitle(dir.name)
.toolbar
ToolbarItem(placement: .navigation)
if path.first != dir
Button("Back")
withAnimation
path.removeLast()
.transition(.move(edge: .trailing))
.listStyle(PlainListStyle())
struct FileCell: View
var file: File
var compact = false
var onTop: Bool
var ns: Namespace.ID
private func previewGrid(_ contents: [File]) -> some View
LazyVGrid(columns: [GridItem(), GridItem()])
ForEach(contents, id: \.self) file in
FileCell(file: file, compact: true, onTop: onTop, ns: ns)
private var icon: some View
RoundedRectangle(cornerRadius: compact ? 10 : 20)
.aspectRatio(1, contentMode: .fill)
.foregroundColor(.accentColor)
.opacity(file.contents == nil ? 1 : 0)
.overlay(
Group
if let symbol = file.symbol
Image(systemName: symbol)
.resizable()
.scaledToFit()
.padding()
.foregroundColor(.primary)
else if onTop,
let contents = file.contents
previewGrid(contents)
)
var body: some View
VStack
icon
.matchedGeometryEffect(id: file, in: ns)
if !compact
Text(file.name)
matchedGeometryEffect
在其body
中位于FileCell
的底部。
我认为可能导致问题的一件事是,由于.transition(.move(edge: .trailing))
,在转换过程中匹配的几何图形不在屏幕上,但使用任何其他转换或根本不使用,也会出现同样的问题。
我还认为 ZStack 可能会导致问题,因为它已经在转换时出现了(请参阅 https://sarunw.com/posts/how-to-fix-zstack-transition-animation-in-swiftui/),但是为了测试而将 ZStack 更改为 VStack 并没有解决问题。
您可能还会注意到 FileCell
上已注释掉的 .animation(.default)
。这确实允许在打开文件夹时进行动画转换,但是动画效果与在该行不存在时关闭文件夹的效果不匹配,并且还会导致文件夹关闭时文件单元格重复,从而导致错误- 外观动画。
编辑:我还想提一下,我没有收到 Multiple inserted views in matched geometry group
错误,所以这也不是问题。
【问题讨论】:
【参考方案1】:我遇到了同样的问题。您需要在 .matchedGeometryEffect() 修饰符之后添加一个动画修饰符。
这两种方式都具有动画效果:
Text("TEST")
.matchedGeometryEffect(id: "test", in: namespace)
.animation(.easeInOut)
这只动画前半部分
Text("TEST")
.animation(.easeInOut)
.matchedGeometryEffect(id: "test", in: namespace)
【讨论】:
【参考方案2】:我进行了一些研究,并能够防止这种行为。所以现在据我了解,matchedGeometryEffect
通常是一个修饰符,它使一个视图具有另一个视图的大小/位置,除了动画目的。
这样我得出结论,如果尺寸以某种方式损坏(它们可能是zero
),动画可能会损坏。解决方案是为两个视图设置显式框架。
someView
.matchedGeometryEffect(id: "view", in: namespace)
.animation(.easeInOut)
.frame(width: 100, height: 100) // or .fixedSize()
【讨论】:
以上是关于SwiftUImatchedGeometryEffect 仅以一种方式设置动画的主要内容,如果未能解决你的问题,请参考以下文章