0

在 SwiftUI 编程的早期,我基本上是在尝试在容器视图中显示卡片列表,显示最上面的卡片......每张卡片都有一个文本视图,当点击卡片视图时,它会从堆栈/容器中删除视图和下一个立即显示。我想在删除当前动画并呈现下一个动画的同时添加过渡动画,但到目前为止我运气不佳。令人惊讶的是,如果我使用 Bool 变量并使用它来显示/隐藏,过渡会按预期工作只有顶部卡片视图,但如果我想同时执行移除和插入,则不是..

import SwiftUI
import Combine

public struct CardsStackView: View {
   @ObservedObject
   public var viewModel: CardsStackViewModel

   public init(cards: [Card]) {
      self.viewModel = CardsStackViewModel(cards: cards)
   }

   public var body: some View {
      ZStack {
        Rectangle()
            .foregroundColor(.white)
            .zIndex(1)
        
        // idea is to remove the top card with slide transition and show next top one with scale transition alongside changing opacity.. wanting to do these both in sync
        cardView(for: viewModel.cards[viewModel.top])
            .zIndex(2)
            .transition(.asymmetric(insertion: .scale.combined(with: .opacity), removal: .slide.combined(with: .opacity)))
       }
    }

    private func cardView(for card: Card) -> some View {
      let view = CardView(card: card)

      viewModel.subscription = view.publisher
        .receive(on: DispatchQueue.main)
        .sink { event in
            if case .dismissed = event {
                // Animating, while changing the top published value in view model inside navigateToNext method
                withAnimation {
                    viewModel.navigateToNext()
                }
            }
      }

      return view
    }
}

这是具有 Binding 变量的视图模型:

import SwiftUI
import Combine

public class CardsStackViewModel: ObservableObject {
   public let cards: [Card]
   var subscription: AnyCancellable?

   @Published
   var top: Int = 0

   public init(cards: [Card]) {
      self.cards = cards
   }
    
   func navigateToNext() {
       guard top < cards.count - 1 else {
          return
       }
    
       top += 1
   }
 }

这是卡片视图

import SwiftUI
import Combine

public enum CardViewEvent {
   case dismissed
}

public struct CardView: View {
   public let card: Card
   public let publisher = PassthroughSubject<CardViewEvent, Never>()

   public init(card: Card) {
      self.card = card
   }

   public var body: some View {
       ZStack(alignment: .leading) {
           Text(card.title)
            .onTapGesture {
               self.publisher.send(.dismissed)
            }
       }
   }
}

卡片型号如下:

import SwiftUI

public struct Card: Identifiable {
   public let id = UUID().uuidString
   public let title: String

   public init(title: String) {
      self.title = title
   }
}

感谢是否有人可以指出它有什么问题。或者这不是理想的方法?

4

1 回答 1

0

结果是通过在 ForEach 循环中添加卡片视图来更改 ZStack 的主体,从而解决了该问题。

import SwiftUI
import Combine

public struct CardsStackView: View {
  @ObservedObject
  public var viewModel: CardsStackViewModel

  public init(cards: [Card]) {
     self.viewModel = CardsStackViewModel(cards: cards)
  }

  public var body: some View {
     ZStack {
       Rectangle()
           .foregroundColor(.white)
    
       // Putting the view in a ForEach loop did the trick for me
       ForEach(0..<viewModel.cards.count, id: \.self) { i in
          if i == viewModel.top {
             cardView(for: viewModel.cards[i])
              .transition(.asymmetric(insertion: .scale.combined(with: 
               .opacity), removal: .slide.combined(with: .opacity)))
          }
       }
     }
   }

   private func cardView(for card: Card) -> some View {
     let view = CardView(card: card)

     viewModel.subscription = view.publisher
       .receive(on: DispatchQueue.main)
       .sink { event in
           if case .dismissed = event {
               // Animating, while changing the top published value in 
               // view model inside navigateToNext method
               withAnimation {
                   viewModel.navigateToNext()
               }
           }
     }

     return view
   }
 }
于 2022-01-05T19:08:08.827 回答