[英]How do I get a replacement view to fade in over top of the old one rather than crossfading with it?
By default, SwiftUI transitions seem to use .opacity
so when a view is appearing/disappearing during an animation it will fade in/out.默认情况下,SwiftUI 过渡似乎使用
.opacity
,因此当视图在 animation 期间出现/消失时,它将淡入/淡出。 This creates a crossfade that is usually quite nice but can sometimes be undesirable when one overlapping view is replacing another.这会创建一个通常非常好的交叉淡入淡出,但有时在一个重叠视图替换另一个视图时可能不受欢迎。
I have a situation where I'd rather have the new view fade in over top of the view that is about to be replaced.我有一种情况,我宁愿让新视图淡入即将被替换的视图之上。 The old view should not change opacity at all but simply disappear when the transition is finished.
旧视图根本不应该改变不透明度,而只是在过渡完成时消失。
I do not want there to be any point during the transition where I'm seeing the old view at anything but 100% opacity.我不希望在过渡期间出现任何一点,我看到的旧视图只有 100% 不透明度。 For example, the default way this is handled results in a point in the middle of the transition where where we're seeing 50% opacity versions of both the back and front view composited over top of each other.
例如,默认的处理方式会导致过渡中间的一个点,在该点我们看到后视图和前视图的 50% 不透明度版本相互叠加。
To be clear, I have one workaround already: I can get the effect I want if I use a ZStack to keep the background view there at all times - this way only the new view fades in (since it's the only view that changes).需要明确的是,我已经有一个解决方法:如果我使用 ZStack 将背景视图始终保留在那里,我可以获得我想要的效果 - 这样只有新视图会淡入(因为它是唯一改变的视图)。 My solution feels wrong, wasteful, and inelegant, though.
不过,我的解决方案感觉是错误的、浪费的和不优雅的。 (The view in the background is going to get composited by the system constantly despite it being totally invisible and unwanted after the actual image loads in. I only want that background view to exist until the transition is complete, but I can't figure out how to make it do that.)
(尽管在实际图像加载后它完全不可见且不需要,但系统会不断合成背景中的视图。我只希望该背景视图存在直到过渡完成,但我无法弄清楚如何让它做到这一点。)
Here's some code that shows what I mean.这是一些代码,说明了我的意思。 The top view appears using the default transition and crossfading but with the code the way I'd expect - when the new view exists, we use it and only it.
顶视图使用默认转换和交叉淡入淡出出现,但代码以我期望的方式出现 - 当新视图存在时,我们使用它并且只使用它。 The bottom appears the way I want it to - but it does so at the expense of keeping the background view there at all times so that only the new view fades when it first appears:
底部以我想要的方式显示 - 但这样做的代价是始终将背景视图保留在那里,这样只有新视图在首次出现时才会消失:
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())
Since I apparently cannot embed a video, here's a link to one of the playground running since it helps to see it to make sense of what I mean here, I think: https://www.dropbox.com/s/scwxfoa9pojo0yq/SwiftUICrossfade.mov?dl=0由于我显然无法嵌入视频,因此这是一个运行的操场的链接,因为它有助于理解我在这里的意思,我认为: https://www.dropbox.com/s/scwxfoa9pojo0yq/SwiftUICrossfade .mov?dl=0
One way might be to apply a custom Transition to the background view.一种方法可能是将自定义转换应用于背景视图。 I only tested this briefly but with this added it looks like the top and bottom views match during their transitions.
我只是简单地测试了这个,但是添加了这个,看起来顶部和底部视图在它们的转换过程中是匹配的。
import SwiftUI
import PlaygroundSupport
struct AllOrNothingTransition: Animatable, ViewModifier {
var animatableData: CGFloat = 0
func body(content: Content) -> some View {
content
.opacity(animatableData == 0 ? 0 : 1)
}
}
extension AnyTransition {
static var allOrNothing: AnyTransition {
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())
If you dislike using ZStack
, consider the combination of Color
+ .overlay
.如果您不喜欢使用
ZStack
,请考虑Color
+ .overlay
的组合。
In this case, Color
can be used as both background color and a placeholder.在这种情况下,
Color
既可以用作背景颜色,也可以用作占位符。
import PlaygroundSupport
import SwiftUI
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) {
Color.red // `Color` as both background and a placeholder
.aspectRatio(CGSize(width: 3600, height: 2085), contentMode: .fit)
.overlay(
AsyncImage(url: imageURL, transaction: transaction) { phase in
if let img = phase.image {
img.resizable()
}
}
)
}
.frame(width: 500)
.padding(10)
.background(Color.yellow)
}
}
PlaygroundPage.current.setLiveView(ContentView())
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.