[英]SplitViewController: How do I replace a the secondary view rather than pushing a new one?
[英]How do I get a replacement view to fade in over top of the old one rather than crossfading with it?
默认情况下,SwiftUI 过渡似乎使用.opacity
,因此当视图在 animation 期间出现/消失时,它将淡入/淡出。 这会创建一个通常非常好的交叉淡入淡出,但有时在一个重叠视图替换另一个视图时可能不受欢迎。
我有一种情况,我宁愿让新视图淡入即将被替换的视图之上。 旧视图根本不应该改变不透明度,而只是在过渡完成时消失。
我不希望在过渡期间出现任何一点,我看到的旧视图只有 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())
由于我显然无法嵌入视频,因此这是一个运行的操场的链接,因为它有助于理解我在这里的意思,我认为: https://www.dropbox.com/s/scwxfoa9pojo0yq/SwiftUICrossfade .mov?dl=0
一种方法可能是将自定义转换应用于背景视图。 我只是简单地测试了这个,但是添加了这个,看起来顶部和底部视图在它们的转换过程中是匹配的。
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())
如果您不喜欢使用ZStack
,请考虑Color
+ .overlay
的组合。
在这种情况下, 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.