我使用SwiftUI制作了一个自定义模式。它可以正常工作,但动画效果很差。
当以慢动作播放时,您会看到ModalContent
的点击 Action 触发后,ModalOverlay
的背景立即消失了。但是,ModalContent
的Text
View 始终保持可见。
谁能告诉我如何防止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)
}
}
}
}
最佳答案
解决方案(由于@JWK):
这可能是一个错误。看起来,在过渡动画期间(当 View 消失时),未涉及到的两个 View (zIndex
和ModalContent
)的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):
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)
}
}
}
}
即使在这种情况下,它也可以按预期工作:
3)但是如果您将
ModalContent
背景颜色更改为绿色(因此ContentView
和ModalContent
都为绿色),则,该问题再次发生(我不会发布其他GIF,但是您可以自己尝试)。4)再举一个例子:如果将iPhone的外观更改为Dark Appearance(iOS 13的新功能),则
ContentView
将自动变为黑色,并且由于ModalView
是白色,因此不会出现问题,一切都会进行美好的。