SwiftUI:自定义模态动画
SwiftUI: Custom Modal Animation
我使用 SwiftUI 制作了一个自定义模式。它工作正常,但动画不稳定。
慢动作播放时,可以看到ModalContent
的背景在触发ModalOverlay
的点击动作后立即消失。但是,ModalContent
的 Text
观看次数始终可见。
谁能告诉我如何防止 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):
这可能是一个错误。看起来,在过渡动画期间(当视图消失时),所涉及的两个视图(ModalContent
和 ModalOverlay
)的 zIndex
未得到遵守。 ModalContent
(应该在 ModalOverlay
前面)实际上在动画开始时移动到 ModalOverlay
下方。要解决此问题,我们可以手动将 zIndex
设置为,例如 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
绿色),问题再次出现(我不会post另一个GIF,但你可以轻松地自己尝试)。
4) 还有一个例子:如果你把你iPhone的外观改成深色外观(iOS13的新特性)你的ContentView
会自动变黑,因为你的 ModalView
是白色的,不会出现问题,一切正常。
我使用 SwiftUI 制作了一个自定义模式。它工作正常,但动画不稳定。
慢动作播放时,可以看到ModalContent
的背景在触发ModalOverlay
的点击动作后立即消失。但是,ModalContent
的 Text
观看次数始终可见。
谁能告诉我如何防止 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):
这可能是一个错误。看起来,在过渡动画期间(当视图消失时),所涉及的两个视图(ModalContent
和 ModalOverlay
)的 zIndex
未得到遵守。 ModalContent
(应该在 ModalOverlay
前面)实际上在动画开始时移动到 ModalOverlay
下方。要解决此问题,我们可以手动将 zIndex
设置为,例如 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
绿色),问题再次出现(我不会post另一个GIF,但你可以轻松地自己尝试)。
4) 还有一个例子:如果你把你iPhone的外观改成深色外观(iOS13的新特性)你的ContentView
会自动变黑,因为你的 ModalView
是白色的,不会出现问题,一切正常。