默认情况下,SwiftUI过渡似乎使用.opacity
,因此当动画期间显示/消失视图时,它将淡入/淡出.这会产生通常非常好的交叉淡入淡出效果,但当一个重叠视图替换另一个重叠视图时,有时可能不太理想.
我有一种情况,我宁愿让新的视图淡入即将被替换的视图的顶部.旧的视图应该根本不会改变不透明度,而只是在过渡完成后消失.
我不希望在过渡期间出现任何时候,除了100%不透明之外,我看到的都是旧的观点.例如,默认的处理方式会导致在过渡中间的一个点上,我们可以看到50%的不透明度版本的后视图和前视图相互叠加.
需要明确的是,我已经有了一个解决办法:如果我使用ZStack始终保持背景视图在那里,我可以获得我想要的效果-这样,只有新的视图淡入(因为它是唯一更改的视图).然而,我的解决方案感觉是错误的、浪费的和不优雅的.(背景中的视图将不断地被系统合成,尽管在实际图像加载后它是完全不可见的和不需要的.我只希望背景视图在转换完成之前一直存在,但我不知道如何让它做到这一点.)
下面是一些代码,说明了我的意思.顶视图使用默认的过渡和交叉淡入淡出显示,但使用代码的方式与我预期的一样--当新视图存在时,我们使用它,并且只使用它.底部以我希望的方式显示-但这样做的代价是始终保持背景视图在那里,以便只有新视图在第一次出现时会褪色:
import SwiftUI
import PlaygroundSupport
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
}
}
.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())
由于我显然不能嵌入视频,这里有一个指向一个正在运行的playground 的链接,因为它有助于看到它来理解我这里的意思,我想:https://www.dropbox.com/s/scwxfoa9pojo0yq/SwiftUICrossfade.mov?dl=0