簡體   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