我使用SwiftUI制作了一个自定义模式。它可以正常工作,但动画效果很差。

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

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

慢动作视频和以下代码:

animation - SwiftUI:自定义模态动画-LMLPHP

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 (zIndexModalContent)的ModalOverlay。实际上,ModalContent(应该在ModalOverlay的前面)在动画开始时被移到ModalOverlay下。为了解决这个问题,我们可以在zIndex View 上手动将ModalContent设置为例如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):

animation - SwiftUI:自定义模态动画-LMLPHP

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)
            }
        }
    }
}

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

animation - SwiftUI:自定义模态动画-LMLPHP

3)但是如果您将ModalContent背景颜色更改为绿色(因此ContentViewModalContent都为绿色),则,该问题再次发生(我不会发布其他GIF,但是您可以自己尝试)。

4)再举一个例子:如果将iPhone的外观更改为Dark Appearance(iOS 13的新功能),则ContentView将自动变为黑色,并且由于ModalView是白色,因此不会出现问题,一切都会进行美好的。

09-27 15:38