我正在使用 SwiftUI 实现一款类似纸牌游戏的纸牌游戏,其中包括堆叠纸牌以及将纸牌从一叠移到另一叠的功能,例如:
\n\n\n\n在此示例中,当我点击“移动卡”按钮时,\xe2\x99\xa0\xef\xb8\x8f9 从第 1 列移动到第 2 列,位于 \xe2\x9d\xa4\xef 的顶部\xb8\x8f10,动画是 \xe2\x99\xa0\xef\xb8\x8f9 从第一列淡出,而新的 \xe2\x99\xa0\xef\xb8\x8f9 同时在第一列淡入第二栏。
\n\n我想要的是 \xe2\x99\xa0\xef\xb8\x8f9 视图本身直接从其原始位置动画到\xe2\x9d\xa4\xef\xb8\x8f10 顶部的最终位置。
\n\n(在我的实际应用程序中,卡片还可以处于其他位置,但实现方式与 类似ColumnView。)
我有一个数据模型,由一个Column包含对象数组的类Card以及相应的ColumnView和CardView结构组成:
public class Column: Identifiable, ObservableObject {\n public let id: Int\n @Published var stack = [Card]()\n\n public init(id: Int, cards: [Card]? = nil) {\n self.id = id\n self.stack = cards ?? []\n }\n\n public func push(_ card: Card) {\n stack.append(card)\n }\n\n public func pop() -> Card {\n guard !stack.isEmpty else { fatalError() }\n return stack.removeLast()\n }\n\n public func orderIndex(for card: Card) -> Int {\n return stack.firstIndex(of: card) ?? -1\n }\n}\n\npublic struct Card: Equatable, Identifiable {\n public let suit: Suit\n public let rank: Rank\n\n public var id: String { return displayTitle }\n\n public init(suit: Suit, rank: Rank) {\n self.suit = suit\n self.rank = rank\n }\n\n public var displayTitle: String {\n return "\\(suit.displayTitle)\\(rank.displayTitle)"\n }\n}\n\nRun Code Online (Sandbox Code Playgroud)\n\npublic struct ColumnView: View {\n @ObservedObject var column: Column\n\n public init(column: Column) {\n self.column = column\n }\n\n public var body: some View {\n ZStack {\n ForEach(column.stack) { card in\n CardView(card: card)\n .offset(x: 0, y: 35*CGFloat(self.column.orderIndex(for: card)))\n .frame(width: 125, height: 187)\n }\n }\n }\n}\n\n\npublic struct CardView: View {\n let card: Card\n\n public init(card: Card) {\n self.card = card\n }\n\n public var body: some View {\n // Implementation that draws the card\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n最后是一个Board包裹对象的对象Column,以及一个对应的BoardView显示(本例中的主屏幕):
struct Board {\n let column1: Column\n let column2: Column\n\n init() {\n // Convenience method for Card.ten.ofDiamonds, etc not shown.\n column1 = Column(id: 0, cards: [\n Card.ten.ofDiamonds,\n Card.nine.ofSpades\n ])\n\n column2 = Column(id: 1, cards: [\n Card.king.ofSpades,\n Card.queen.ofDiamonds,\n Card.jack.ofClubs,\n Card.ten.ofHearts\n ])\n }\n\n func moveCard() {\n let card = column1.pop()\n column2.push(card)\n }\n}\n\nstruct BoardView: View {\n let board = Board()\n\n var body: some View {\n ZStack(alignment: .center) {\n Rectangle()\n .foregroundColor(.green)\n .frame(maxWidth: .infinity, maxHeight: .infinity)\n\n HStack(spacing: 20) {\n ColumnView(column: board.column1)\n ColumnView(column: board.column2)\n }\n\n Button(action: {\n withAnimation {\n self.board.moveCard()\n }\n }) {\n Text("Move Card")\n .font(.system(size: 20, weight: .bold, design: .default))\n .foregroundColor(.white)\n }\n .offset(x: 0, y: 300)\n }.edgesIgnoringSafeArea(.all)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n淡出/淡入发生的原因是有道理的ColumnView- 从第一个角度来看,column1丢失了一张卡片(卡片已从column1阵列中删除),因此它会以淡出的方式过渡出去;从第二个ColumnView角度来看,column2获得了一张卡片(卡片已添加到阵列中column2),因此它会以淡入的方式过渡。
问题是,有没有一种方法可以实现我想要的,而不必完全重新架构我已经设置的数据模型和视图层次结构,其中Cards 位于Columns 内,CardViews 位于ColumnViews 内?按原样使用该数据模型确实很有帮助,并且将数据模型直接映射到视图层次结构也很有帮助。
我可以想象某种替代视图层次结构,其中只有 52CardView个,并且根据它们所在的列或位置对位置进行某种修饰符,但这感觉非常老套。我考虑过某种自定义transition,但过渡是用于插入或删除视图的。有没有办法告诉 SwiftUI 该Card结构不是被删除和重新添加,而是被移动,从而执行匹配的移动动画?
(从对该问题的评论复制)
\n\n从 Xcode 11.3、iOS 13.3 开始:
\n\n遗憾的是,我向 Apple 提交了 DTS 票证,他们说 \xe2\x80\x99 目前无法在 SwiftUI 中执行我想要执行的操作。我最终将 \xe2\x80\x9call 52 卡\xe2\x80\x9d 数组公开为计算属性,并使用锚点首选项和偏移量来设置卡\xe2\x80\x99 位置以允许它们动画。(换句话说,本质上是我\xe2\x80\x99d 希望避免的解决方案。)希望这在 iOS 14 / Xcode 12 中得到改进。
\n| 归档时间: |
|
| 查看次数: |
2946 次 |
| 最近记录: |