繁体   English   中英

如何让替换视图淡入旧视图而不是交叉淡入淡出?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM