我正在尝试使用SwiftUI构建动画。
Start: [ A ][ B ][ D ]
End: [ A ][ B ][ C ][ D ]
动画的关键元素是:
使用SwiftUI满足所有这些要求非常困难,但是过去能够通过自动布局实现类似的效果。
我的第一次尝试是使用带有layoutPriorities的HStack
进行的过渡。并没有真正接近,因为它会影响动画过程中C的宽度。
我的第二个尝试是保留HStack
,但是使用带有非对称移动动画的过渡。这确实很接近,但是动画中B和C的移动并没有产生C恰好位于B下方的效果。
我最近的尝试是放弃依赖HStack
的两个动画视图,而改用ZStack
。通过此设置,可以结合使用offset
和padding
来使动画完美。 但是,只有将B和C的帧大小设为已知值,我才能正确解决问题。
有没有人对如何实现这种效果有任何想法?无需为B和C设置固定的帧大小?
答案 0 :(得分:3)
自从我最初回答这个问题以来,我一直在研究GeometryReader,View Preferences和Anchor Preferences。我整理了详细的解释,以作进一步阐述。您可以在以下网址阅读它:https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
一旦将CCCCCCCC视图几何放入textRect变量中,其余的操作就很简单。您只需使用.offset(x :)修饰符和clipped()。
import SwiftUI
struct RectPreferenceKey: PreferenceKey {
static var defaultValue = CGRect()
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
typealias Value = CGRect
}
struct ContentView : View {
@State private var textRect = CGRect()
@State private var slideOut = false
var body: some View {
return VStack {
HStack(spacing: 0) {
Text("AAAAAA")
.font(.largeTitle)
.background(Color.yellow)
.zIndex(4)
Text("BBBB")
.font(.largeTitle)
.background(Color.red)
.zIndex(3)
Text("I am a very long text")
.zIndex(2)
.font(.largeTitle)
.background(GeometryGetter())
.background(Color.green)
.offset(x: slideOut ? 0.0 : -textRect.width)
.clipped()
.onPreferenceChange(RectPreferenceKey.self) { self.textRect = $0 }
Text("DDDDDDDDDDDDD").font(.largeTitle)
.zIndex(1)
.background(Color.blue)
.offset(x: slideOut ? 0.0 : -textRect.width)
}.offset(x: slideOut ? 0.0 : +textRect.width / 2.0)
Divider()
Button(action: {
withAnimation(.basic(duration: 1.5)) {
self.slideOut.toggle()
}
}, label: {
Text("Animate Me")
})
}
}
}
struct GeometryGetter: View {
var body: some View {
GeometryReader { geometry in
return Rectangle()
.fill(Color.clear)
.preference(key: RectPreferenceKey.self, value:geometry.frame(in: .global))
}
}
}
答案 1 :(得分:1)
很难说出您到底要干什么或什么不起作用。如果您显示想出或共享代码的“错误”动画,将更容易为您提供帮助。
无论如何,这是需要注意的。我认为它确实可以满足您的指定要求,尽管它当然并不完美:
观察:
动画依赖于这样的假设:(A)和(B)一起比(C)宽。否则,(C)的部分将在动画开始时显示在A的左侧。
类似地,动画依赖于以下事实:视图之间没有间距。否则,当(C)宽于(B)时,它将出现在(B)的左侧。
通过在层次结构中放置不透明的底视图以使其位于(A),(B)和(D)以下但在(C)以上,可以解决这两个问题。但是我还没有考虑到这一点。
HStack
的展开速度似乎比(C)滑入的快,这就是为什么白色部分短暂出现的原因。我没有设法消除这一点。我尝试将相同的animation(.basic())
修饰符添加到HStack
,过渡,withAnimation
调用和VStack
,但这无济于事。
代码:
import SwiftUI
struct ContentView: View {
@State var thirdViewIsVisible: Bool = false
var body: some View {
VStack(alignment: .leading, spacing: 20) {
HStack(spacing: 0) {
Text("Lorem ").background(Color.yellow)
.zIndex(1)
Text("ipsum ").background(Color.red)
.zIndex(1)
if thirdViewIsVisible {
Text("dolor sit ").background(Color.green)
.zIndex(0)
.transition(.move(edge: .leading))
}
Text("amet.").background(Color.blue)
.zIndex(1)
}
.border(Color.red, width: 1)
Button(action: { withAnimation { self.thirdViewIsVisible.toggle() } }) {
Text("Animate \(thirdViewIsVisible ? "out" : "in")")
}
}
.padding()
.border(Color.green, width: 1)
}
}