SwiftUI:自定义模态动画

JWK*_*JWK 6 animation modal-dialog swiftui

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

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

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

慢动作视频和代码如下:

在此处输入图片说明

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

sup*_*cio 0

解决方案(感谢@JWK):

这可能是一个错误。看来,在过渡动画期间(当视图消失时)所zIndex涉及的两个视图( theModalContent和 the ModalOverlay)不受尊重。(ModalContent应该位于 前面的)实际上在动画开始时ModalOverlay移到了 下方。ModalOverlay要解决此问题,我们可以zIndex在视图上手动设置,例如 1 ModalContent

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

调查得出解决方案

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

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

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

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

4)又一个例子:如果你将iPhone的外观更改为深色外观(iOS 13的新功能),你的iPhoneContentView会自动变成黑色,而由于你的ModalView是白色,所以不会出现问题,一切都会正常。