如何阻止 SwiftUI DragGesture 为子视图设置动画 [英] How to stop SwiftUI DragGesture from animating subviews
问题描述
我正在构建一个自定义模态,当我拖动模态时,任何附加了动画的子视图都会在我拖动时进行动画处理.我如何阻止这种情况发生?
I'm building a custom modal and when I drag the modal, any subviews that have animation's attached, they animate while I'm dragging. How do I stop this from happening?
我想过传递一个带有 isDragging
标志的 @EnvironmentObject
,但它的可扩展性不是很好(并且不适用于自定义 ButtonStyle
代码>s)
I thought about passing down an @EnvironmentObject
with a isDragging
flag, but it's not very scalable (and doesn't work well with custom ButtonStyle
s)
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
.showModal(isShowing: .constant(true))
}
}
extension View {
func showModal(isShowing: Binding<Bool>) -> some View {
ViewOverlay(isShowing: isShowing, presenting: { self })
}
}
struct ViewOverlay<Presenting>: View where Presenting: View {
@Binding var isShowing: Bool
let presenting: () -> Presenting
@State var bottomState: CGFloat = 0
var body: some View {
ZStack(alignment: .center) {
presenting().blur(radius: isShowing ? 1 : 0)
VStack {
if isShowing {
Container()
.background(Color.red)
.offset(y: bottomState)
.gesture(
DragGesture()
.onChanged { value in
bottomState = value.translation.height
}
.onEnded { _ in
if bottomState > 50 {
withAnimation {
isShowing = false
}
}
bottomState = 0
})
.transition(.move(edge: .bottom))
}
}
}
}
}
struct Container: View {
var body: some View {
// I want this to not animate when dragging the modal
Text("CONTAINER")
.frame(maxWidth: .infinity, maxHeight: 200)
.animation(.spring())
}
}
更新:
extension View {
func animationsDisabled(_ disabled: Bool) -> some View {
transaction { (tx: inout Transaction) in
tx.animation = tx.animation
tx.disablesAnimations = disabled
}
}
}
Container()
.animationsDisabled(isDragging || bottomState > 0)
在现实生活中,Container 包含一个按钮,按钮的按下状态有动画
In real life the Container contains a button with an animation on its pressed state
struct MyButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.scaleEffect(configuration.isPressed ? 0.9 : 1)
.animation(.spring())
}
}
在子视图中添加了 animationsDisabled 函数,这实际上会阻止子视图在拖动过程中移动.
Added the animationsDisabled function to the child view which does in fact stop the children moving during the drag.
它不会在最初滑入或消失时停止动画.
What it doesn't do is stop the animation when the being initially slide in or dismissed.
有没有办法知道视图何时基本上没有移动/过渡?
Is there a way to know when a view is essentially not moving / transitioning?
推荐答案
理论上 SwiftUI 不应该在这种情况下转换动画,但是我不确定这是否是一个错误 - 我不会以那种通用方式在 Container 中使用动画.我使用动画越多,就越倾向于将它们直接连接到特定值.
Theoretically SwiftUI should not translate animation in this case, however I'm not sure if this is a bug - I would not use animation in Container in that generic way. The more I use animations the more tend to join them directly to specific values.
无论如何...这里有一个可能的解决方法 - 通过在中间注入不同的宿主控制器来破坏动画可见性.
Anyway... here is possible workaround - break animation visibility by injecting different hosting controller in a middle.
使用 Xcode 12/iOS 14 测试
Tested with Xcode 12 / iOS 14
struct ViewOverlay<Presenting>: View where Presenting: View {
@Binding var isShowing: Bool
let presenting: () -> Presenting
@State var bottomState: CGFloat = 0
var body: some View {
ZStack(alignment: .center) {
presenting().blur(radius: isShowing ? 1 : 0)
VStack {
Color.clear
if isShowing {
HelperView {
Container()
.background(Color.red)
}
.offset(y: bottomState)
.gesture(
DragGesture()
.onChanged { value in
bottomState = value.translation.height
}
.onEnded { _ in
if bottomState > 50 {
withAnimation {
isShowing = false
}
}
bottomState = 0
})
.transition(.move(edge: .bottom))
}
Color.clear
}
}
}
}
struct HelperView<Content: View>: UIViewRepresentable {
let content: () -> Content
func makeUIView(context: Context) -> UIView {
let controller = UIHostingController(rootView: content())
return controller.view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
这篇关于如何阻止 SwiftUI DragGesture 为子视图设置动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!