默认情况下,SwiftUI 似乎使用过渡.opacity,因此当视图在动画期间出现/消失时,它会淡入/淡出。这会创建一种交叉淡入淡出效果,通常效果很好,但当一个重叠视图替换另一个视图时,有时可能会出现不良效果。
我遇到的情况是,我\xe2\x80\x99d 宁愿让新视图淡入即将被替换的视图之上。旧视图根本不应该改变不透明度,而应该在转换完成后消失。
\n我不希望在转换过程中的任何时刻我看到旧视图的透明度只有 100%。例如,默认的处理方式会导致过渡中间的某个点,在该点我们会看到后视图和前视图的 50% 不透明度版本相互叠加。
\n需要明确的是,我已经有了一个解决方法:如果我使用 ZStack 始终将背景视图保留在那里,我就可以获得我想要的效果 - 这样只有新视图会淡入(因为它是唯一的视图)变化)。不过,我的解决方案感觉是错误的、浪费的、不优雅的。(背景中的视图将不断地由系统合成,尽管在实际图像加载后它完全不可见且不需要。我只希望背景视图存在直到转换完成,但我无法理解知道如何让它做到这一点。)
\n这是一些显示我的意思的代码。顶部视图使用默认的过渡和交叉淡入淡出显示,但代码按照我期望的方式显示 - 当新视图存在时,我们只使用它。底部按照我想要的方式显示 - 但这样做的代价是始终将背景视图保留在那里,以便只有新视图在第一次出现时才会消失:
\nimport SwiftUI\nimport PlaygroundSupport\n\nstruct ContentView: View {\n let transaction = Transaction(animation: .linear(duration: 10))\n let imageURL = URL(string: "https://www.nasa.gov/sites/default/files/thumbnails/image/main_image_star-forming_region_carina_nircam_final-5mb.jpg")!\n \n var body: some View {\n VStack(spacing: 10) {\n AsyncImage(url: imageURL, transaction: transaction) { phase in\n if let img = phase.image {\n img.resizable()\n } else {\n Color.red\n }\n }\n .aspectRatio(CGSize(width: 3600, height: 2085), contentMode: .fit)\n\n AsyncImage(url: imageURL, transaction: transaction) { phase in\n ZStack {\n Color.red\n \n if let img = phase.image {\n img.resizable()\n }\n }\n }\n .aspectRatio(CGSize(width: 3600, height: 2085), contentMode: .fit)\n }\n .frame(width: 500)\n .padding(10)\n .background(Color.yellow)\n }\n}\n\nPlaygroundPage.current.setLiveView(ContentView())\nRun Code Online (Sandbox Code Playgroud)\n由于我显然无法嵌入视频,这里有一个正在运行的操场之一的链接,因为它有助于看到它以理解我在这里的意思,我认为:https ://www.dropbox.com/s/scwxfoa9pojo0yq /SwiftUICrossfade.mov?dl=0
\n一种方法可能是将自定义过渡应用到背景视图。我只是简单地测试了这一点,但添加了这一点后,顶部视图和底部视图在转换过程中看起来是匹配的。
import SwiftUI
import PlaygroundSupport
struct AllOrNothingTransition: Animatable, ViewModifier {
var animatableData: CGFloat = 0
func body(content: Content) -> some View {
// The goal is for `content` to remain in place, unchanged,
// until the transition completes and it is removed, so we
// simply pass it back unchanged regardless of the value
// of `animatableData`.
content
}
}
extension AnyTransition {
static var allOrNothing: AnyTransition {
// For this use case, the specific values passed in as `animatableData`
// are not important, as long as they differ from each other.
// SwiftUI needs to have differing values to interpolate between,
// otherwise it will assume there is nothing to animate and
// remove the transitioning view immediately at the start of
// of the transition (this is based on observation, not a knowledge
// of the inner workings of SwiftUI).
AnyTransition.modifier(
active: AllOrNothingTransition(animatableData: 0),
identity: AllOrNothingTransition(animatableData: 1)
)
}
}
struct ContentView: View {
let transaction = Transaction(animation: .linear(duration: 10))
let imageURL = URL(string: "https://www.nasa.gov/sites/default/files/thumbnails/image/main_image_star-forming_region_carina_nircam_final-5mb.jpg")!
var body: some View {
VStack(spacing: 10) {
AsyncImage(url: imageURL, transaction: transaction) { phase in
if let img = phase.image {
img.resizable()
} else {
Color.red
.transition(.allOrNothing)
}
}
.aspectRatio(CGSize(width: 3600, height: 2085), contentMode: .fit)
AsyncImage(url: imageURL, transaction: transaction) { phase in
ZStack {
Color.red
if let img = phase.image {
img.resizable()
}
}
}
.aspectRatio(CGSize(width: 3600, height: 2085), contentMode: .fit)
}
.frame(width: 500)
.padding(10)
.background(Color.yellow)
}
}
PlaygroundPage.current.setLiveView(ContentView())
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1220 次 |
| 最近记录: |