swiftui - SwiftUI:呈现具有从帧到帧的自定义过渡的模态视图?
问题描述
UIViewControllerTransitioningDelegate
当使用and呈现 ViewController 时,我试图复制我在 UIKit 中所做的事情UIViewControllerAnimatedTransitioning
。
因此,例如,从看起来像这样的视图:
我想呈现一个视图(我会说一个模态视图,但我不确定这是否是在 SwiftUI 中进行此操作的正确方法),它从视图 A 变为:
所以,我需要视图 B 从匹配视图 A 的帧逐渐淡入到几乎全屏。这个想法是用户点击 A,就好像它想将它扩展到它的细节(视图 B)。
我研究了 SwiftUI 转换,如下所示:
extension AnyTransition {
static var moveAndFade: AnyTransition {
let insertion = AnyTransition.move(edge: .trailing)
.combined(with: .opacity)
let removal = AnyTransition.scale
.combined(with: .opacity)
return .asymmetric(insertion: insertion, removal: removal)
}
}
所以,我认为我需要建立一个自定义过渡。但是,我不知道该怎么做,但对此还是陌生的。
我将如何建立一个过渡来处理所描述的案例?能够拥有一个from frame
和一个to frame
...?
这是在 SwiftUI 中思考它的正确方式吗?
新的信息:
我已经测试过了matchedGeometryEffect
。
例子:
struct TestParentView: View {
@State private var expand = false
@Namespace private var shapeTransition
var body: some View {
VStack {
if expand {
// Rounded Rectangle
Spacer()
RoundedRectangle(cornerRadius: 50.0)
.matchedGeometryEffect(id: "circle", in: shapeTransition)
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 300)
.padding()
.foregroundColor(Color(.systemGreen))
.animation(.easeIn)
.onTapGesture {
expand.toggle()
}
} else {
// Circle
RoundedRectangle(cornerRadius: 50.0)
.matchedGeometryEffect(id: "circle", in: shapeTransition)
.frame(width: 100, height: 100)
.foregroundColor(Color(.systemOrange))
.animation(.easeIn)
.onTapGesture {
expand.toggle()
}
Spacer()
}
}
}
}
看起来matchedGeometryEffect
可能是这项工作的工具。但是,即使使用matchedGeometryEffect
,我仍然无法解决这两个问题:
- 如何包含淡入/淡出动画?
- 查看 的行为
matchedGeometryEffect
,当我“关闭”视图 B 时,视图 B 立即消失,我们看到的动画是视图 A 从 B 回到视图 A 的原始帧。我实际上希望视图 B 在淡出时缩小到 A 所在的位置。
解决方案
您必须在.matchedGeometryEffect
要转换的两个视图上使用修饰符。
这是一个例子:
struct MatchedGeometryEffect: View {
@Namespace var nspace
@State private var toggle: Bool = false
var body: some View {
HStack {
if toggle {
VStack {
Rectangle()
.foregroundColor(Color.green)
.matchedGeometryEffect(id: "animation", in: nspace)
.frame(width: 300, height: 300)
Spacer()
}
}
if !toggle {
VStack {
Spacer()
Rectangle()
.foregroundColor(Color.blue)
.matchedGeometryEffect(id: "animation", in: nspace)
.frame(width: 50, height: 50)
}
}
}
.padding()
.overlay(
Button("Switch") { withAnimation(.easeIn(duration: 2)) { toggle.toggle() } }
)
}
}
图片应该是 GIF
使用此修饰符的主要两部分是id
和namespace
。
您尝试匹配的id
两个 View 必须相同。然后它们也必须在同一个命名空间中。@Namespace
命名空间使用属性包装器在顶部声明。在我的示例中,我使用了“动画”,但它实际上可以是任何东西,最好是能够从其他类型的动画中唯一识别视图的东西。
另一个重要的信息是控制视图显示/隐藏的'''@State'''变量是动画的。这是通过使用withAnimation { toggle.toggle() }
.
我对此也很陌生,因此有关更多信息,您可以阅读我从 Swift-UI 实验室找到的这篇文章: