SwiftUI - 动画 ZStack 大小时未对齐的矩形
SwiftUI - Misaligned Rectangles when Animating Size of ZStack
我正在 SwiftUI 中创建一个自定义的“披露组”,它实现了一种“滑动删除”功能。它的工作原理是将两个矩形堆叠在一起,底部的矩形是滑动时看到的红色“删除”按钮。我还有一个布尔值,用于标记组件是否“扩展”,即具有更大的尺寸。这是一段视频:
如您所见,该组件在点击时会变大,在拖动时会显示一个红色的“删除”按钮。但是,展开组件时,你可以看到底部显示了删除矩形的一部分。下面是实现,我不确定为什么两个矩形不完全聚在一起。有谁知道我怎样才能避免这个故障?
MRE:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing: 30) {
TestDisclosure()
TestDisclosure()
TestDisclosure()
Spacer()
}
}
}
struct TestDisclosure: View {
@State var expanded: Bool = false
@State var isDeleting: Bool = false
@State var horzdrag: CGFloat = 0 // the horizontal translation of the drag
@State var predictedEnd: CGFloat = 0 // the predicted end translation of the drag
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.red)
label
.clipped()
.offset(x: getOffset(horzdrag: horzdrag))
.animation(.spring(), value: horzdrag)
}
.offset(x: isDeleting ? -400 : 0)
.animation(.spring(), value: isDeleting)
.transition(.move(edge: .leading))
.gesture(
DragGesture()
.onChanged { gesture in
onDragChange(gesture: gesture)
}
.onEnded { _ in
onDragEnd()
}
)
.cornerRadius(15)
.padding(.horizontal)
.onTapGesture {
withAnimation(.spring()) {
expanded.toggle()
}
}
.frame(maxHeight: expanded ? 150 : 85)
.clipped()
}
private var label: some View {
ZStack {
Rectangle()
.foregroundColor(.teal)
VStack {
HStack {
Text("Test")
Spacer()
Text("1 unit")
Text("12 units")
}
.padding(.horizontal)
}
}
}
private func onDragChange(gesture: DragGesture.Value) {
horzdrag = gesture.translation.width
predictedEnd = gesture.predictedEndTranslation.width
}
private func onDragEnd() {
if getOffset(horzdrag: horzdrag) <= -400 {
withAnimation(.spring()) {
isDeleting = true
}
}
horzdrag = .zero
}
// used to calculate how far to move the teal rectangle
private func getOffset(horzdrag: CGFloat) -> CGFloat {
if isDeleting {
return -400
} else if horzdrag < -165 {
return -400
} else if predictedEnd < -60 && horzdrag == 0 {
return -80
} else if predictedEnd < -60 {
return horzdrag
} else if predictedEnd < 50 && horzdrag > 0 && (-80 + horzdrag <= 0) {
return -80 + horzdrag
} else if horzdrag < 0 {
return horzdrag
} else {
return 0
}
}
}
这看起来像是同一动画(spring 在这种情况下)应用于不同属性的干扰,`因为在拖动(偏移)尚未完成时应用点击(折叠)时观察到效果。
我不确定这是否是一个错误,但解决方法是使用不同类型的动画。
这是一个解决方法 - 使用 default
而不是 spring
。
使用 Xcode 13.3 / iOS 15.4
测试
label
.clipped()
.offset(x: getOffset(horzdrag: horzdrag))
.animation(.default, value: horzdrag) // << here !!
我正在 SwiftUI 中创建一个自定义的“披露组”,它实现了一种“滑动删除”功能。它的工作原理是将两个矩形堆叠在一起,底部的矩形是滑动时看到的红色“删除”按钮。我还有一个布尔值,用于标记组件是否“扩展”,即具有更大的尺寸。这是一段视频:
如您所见,该组件在点击时会变大,在拖动时会显示一个红色的“删除”按钮。但是,展开组件时,你可以看到底部显示了删除矩形的一部分。下面是实现,我不确定为什么两个矩形不完全聚在一起。有谁知道我怎样才能避免这个故障?
MRE:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing: 30) {
TestDisclosure()
TestDisclosure()
TestDisclosure()
Spacer()
}
}
}
struct TestDisclosure: View {
@State var expanded: Bool = false
@State var isDeleting: Bool = false
@State var horzdrag: CGFloat = 0 // the horizontal translation of the drag
@State var predictedEnd: CGFloat = 0 // the predicted end translation of the drag
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.red)
label
.clipped()
.offset(x: getOffset(horzdrag: horzdrag))
.animation(.spring(), value: horzdrag)
}
.offset(x: isDeleting ? -400 : 0)
.animation(.spring(), value: isDeleting)
.transition(.move(edge: .leading))
.gesture(
DragGesture()
.onChanged { gesture in
onDragChange(gesture: gesture)
}
.onEnded { _ in
onDragEnd()
}
)
.cornerRadius(15)
.padding(.horizontal)
.onTapGesture {
withAnimation(.spring()) {
expanded.toggle()
}
}
.frame(maxHeight: expanded ? 150 : 85)
.clipped()
}
private var label: some View {
ZStack {
Rectangle()
.foregroundColor(.teal)
VStack {
HStack {
Text("Test")
Spacer()
Text("1 unit")
Text("12 units")
}
.padding(.horizontal)
}
}
}
private func onDragChange(gesture: DragGesture.Value) {
horzdrag = gesture.translation.width
predictedEnd = gesture.predictedEndTranslation.width
}
private func onDragEnd() {
if getOffset(horzdrag: horzdrag) <= -400 {
withAnimation(.spring()) {
isDeleting = true
}
}
horzdrag = .zero
}
// used to calculate how far to move the teal rectangle
private func getOffset(horzdrag: CGFloat) -> CGFloat {
if isDeleting {
return -400
} else if horzdrag < -165 {
return -400
} else if predictedEnd < -60 && horzdrag == 0 {
return -80
} else if predictedEnd < -60 {
return horzdrag
} else if predictedEnd < 50 && horzdrag > 0 && (-80 + horzdrag <= 0) {
return -80 + horzdrag
} else if horzdrag < 0 {
return horzdrag
} else {
return 0
}
}
}
这看起来像是同一动画(spring 在这种情况下)应用于不同属性的干扰,`因为在拖动(偏移)尚未完成时应用点击(折叠)时观察到效果。
我不确定这是否是一个错误,但解决方法是使用不同类型的动画。
这是一个解决方法 - 使用 default
而不是 spring
。
使用 Xcode 13.3 / iOS 15.4
label
.clipped()
.offset(x: getOffset(horzdrag: horzdrag))
.animation(.default, value: horzdrag) // << here !!