简体   繁体   English

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

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

相关问题 SplitViewController:如何替换辅助视图而不是推送新视图? - SplitViewController: How do I replace a the secondary view rather than pushing a new one? View 被拴在安全区域边缘顶部而不是 superview,我将如何拴在 superview 顶部? - View is tethered to the safe area margin top rather than superview, how would I tether to the superview top? 如何每次将新数据保存到我的plist文件中,而不是替换旧数据? - How can I save each time a new data to my plist file rather than replace the old one? 如何获取显示的视图控制器的顶视图 - How do I get the top view which is a presented view controller 如何使用多个滚动条从ui选择器视图中获取数据 - How do i get the data from a ui picker view with more than one scroller in swift 如何使此淡入淡出动画起作用? - How do I get this fade animation to work? 如何在 SwiftUI 中淡化 SF 符号的顶部或底部边缘? - How Do I Fade the Top or Bottom Edge of a SF Symbol in SwiftUI? 如何查看超过两周的Apple App Store崩溃报告? - How do I view Apple App Store crash reports older than two weeks old? 如何在视图控制器之间进行淡入淡出/无过渡 - How do I do a Fade/No transition between view controllers 如何添加到UINavigationController推送动画,而不是替换它? - How do I add to a UINavigationController push animation, rather than replace it?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM