animation - SwiftUI:自定义模态动画

标签 animation modal-dialog swiftui

我使用 SwiftUI 制作了一个自定义模式。它工作正常,但动画很不稳定。

以慢动作播放时,您可以看到 ModalContent 的背景在触发 ModalOverlay 的点击操作后立即消失。但是,ModalContentText View 始终保持可见。

谁能告诉我如何防止 ModalContent 的背景过早消失?

慢动作视频和代码如下:

enter image description here

import SwiftUI

struct ContentView: View {
    @State private var isShowingModal = false

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Button(
                    action: { withAnimation { self.isShowingModal = true } },
                    label: { Text("Show Modal") }
                )

                ZStack {
                    if self.isShowingModal {
                        ModalOverlay(tapAction: { withAnimation { self.isShowingModal = false } })
                        ModalContent().transition(.move(edge: .bottom))
                    }
                }.edgesIgnoringSafeArea(.all)
            }
        }
    }
}

struct ModalOverlay: View {
    var color = Color.black.opacity(0.4)
    var tapAction: (() -> Void)? = nil

    var body: some View {
        color.onTapGesture { self.tapAction?() }
    }
}

struct ModalContent: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Spacer()
                VStack(spacing: 16) {
                    Text("Item 1")
                    Text("Item 2")
                    Text("Item 3")
                }
                .frame(width: geometry.size.width)
                .padding(.top, 16)
                .padding(.bottom, geometry.safeAreaInsets.bottom)
                .background(Color.white)
            }
        }
    }
}

最佳答案

解决方案(感谢@JWK):

这可能是一个错误。看起来,在过渡动画期间(当 View 消失时)涉及的两个 View (ModalContentModalOverlay)的 zIndex不被尊重。 ModalContent(应该位于 ModalOverlay 前面)实际上在动画开始时移动到 ModalOverlay 下方。要解决此问题,我们可以在 ModalContent View 上手动将 zIndex 设置为 1。

struct ContentView: View {
    @State private var isShowingModal = false

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Button(
                    action: { withAnimation { self.isShowingModal = true } },
                    label: { Text("Show Modal") }
                )

                ZStack {
                    if self.isShowingModal {
                        ModalOverlay(tapAction: { withAnimation(.easeOut(duration: 5)) { self.isShowingModal = false } })
                        ModalContent()
                            .transition(.move(edge: .bottom))
                            .zIndex(1)
                    }
                }.edgesIgnoringSafeArea(.all)
            }
        }
    }
}

通过调查得出解决方案

SwiftUI 中的过渡动画仍然存在一些问题。我认为这是一个错误。我很确定,因为:

1) 您是否尝试过将 ModalContent 的背景颜色从白色更改为绿色?

struct ModalContent: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Spacer()
                VStack(spacing: 16) {
                    Text("Item 1")
                    Text("Item 2")
                    Text("Item 3")
                }
                .frame(width: geometry.size.width)
                .padding(.top, 16)
                .padding(.bottom, geometry.safeAreaInsets.bottom)
                .background(Color.green)
            }
        }
    }
}

这样就可以了(参见下面的 GIF):

enter image description here

2) 导致错误发生的另一种方法是将 ContentView 的背景颜色更改为绿色,将 ModalContent 保留为白色:

struct ContentView: View {
    @State private var isShowingModal = false

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Button(
                    action: { withAnimation(.easeOut(duration: 5)) { self.isShowingModal = true } },
                    label: { Text("Show Modal") }
                )

                ZStack {
                    if self.isShowingModal {
                        ModalOverlay(tapAction: { withAnimation(.easeOut(duration: 5)) { self.isShowingModal = false } })
                        ModalContent().transition(.move(edge: .bottom))
                    }
                }
            }
        }
        .background(Color.green)
        .edgesIgnoringSafeArea(.all)
    }
}

struct ModalOverlay: View {
    var color = Color.black.opacity(0.4)
    var tapAction: (() -> Void)? = nil

    var body: some View {
        color.onTapGesture { self.tapAction?() }
    }
}

struct ModalContent: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Spacer()
                VStack(spacing: 16) {
                    Text("Item 1")
                    Text("Item 2")
                    Text("Item 3")
                }
                .frame(width: geometry.size.width)
                .padding(.top, 16)
                .padding(.bottom, geometry.safeAreaInsets.bottom)
                .background(Color.white)
            }
        }
    }
}

即使在这种情况下,它也能按预期工作:

enter image description here

3) 但是如果您将 ModalContent 背景颜色更改为绿色(这样您就拥有 ContentViewModalContent 绿色),问题再次出现(我不会再发布另一个 GIF,但你可以轻松地自己尝试一下)。

4) 另一个例子:如果您将 iPhone 的外观更改为深色外观(iOS 13 的新功能),您的 ContentView 将自动变为黑色,并且,因为您的 ModalView 为白色,问题不会发生,一切顺利。

关于animation - SwiftUI:自定义模态动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57902636/

相关文章:

javascript - 导入到 Three.js 后,Blender 动画变得扭曲

ios - 如何使用 UIViewPropertyAnimator 为 View 层阴影设置动画

javascript - 如何在 map 函数中使用模态?

javascript - 这个浏览器内的轻量级动画是如何完成的?

ios - 使用贝塞尔路径作为剪贴蒙版

dialog - dialog.showMessageBoxSync(null,options)被隐藏

javascript - 从远程页面关闭jQuery模式对话框

类似于 Slack 的 SwiftUI 导航

ios - 测试 SwiftUI 正文 View 和属性

ios - SwiftUI - Canvas 超出屏幕宽度 - 应用被拒绝