我正在使用SwiftUI来实现类似于纸牌的纸牌游戏,其中包括堆叠纸牌以及将纸牌从一个纸堆移到另一个纸堆的功能,例如:
在这里的示例中,当我点击“移动卡”按钮时,♠️9从第1列移至第2列,位于❤️10的顶部,动画是the️9从第一列,而新的♠️9同时出现在第二列。
我想要的是为了使9️9视图自身在❤️10上方将从其原始位置直接动画化为最终位置。
(在我的实际应用中,卡片也可以位于其他位置,但是实现方式类似于ColumnView
。)
我有一个数据模型,该数据模型由一个Column
类组成,该类包含Card
对象的数组以及相应的ColumnView
和CardView
结构:
public class Column: Identifiable, ObservableObject {
public let id: Int
@Published var stack = [Card]()
public init(id: Int, cards: [Card]? = nil) {
self.id = id
self.stack = cards ?? []
}
public func push(_ card: Card) {
stack.append(card)
}
public func pop() -> Card {
guard !stack.isEmpty else { fatalError() }
return stack.removeLast()
}
public func orderIndex(for card: Card) -> Int {
return stack.firstIndex(of: card) ?? -1
}
}
public struct Card: Equatable, Identifiable {
public let suit: Suit
public let rank: Rank
public var id: String { return displayTitle }
public init(suit: Suit, rank: Rank) {
self.suit = suit
self.rank = rank
}
public var displayTitle: String {
return "\(suit.displayTitle)\(rank.displayTitle)"
}
}
public struct ColumnView: View {
@ObservedObject var column: Column
public init(column: Column) {
self.column = column
}
public var body: some View {
ZStack {
ForEach(column.stack) { card in
CardView(card: card)
.offset(x: 0, y: 35*CGFloat(self.column.orderIndex(for: card)))
.frame(width: 125, height: 187)
}
}
}
}
public struct CardView: View {
let card: Card
public init(card: Card) {
self.card = card
}
public var body: some View {
// Implementation that draws the card
}
}
最后是一个Board
对象,该对象包装了Column
对象,并显示了一个对应的BoardView
(在此示例中为主屏幕):
struct Board {
let column1: Column
let column2: Column
init() {
// Convenience method for Card.ten.ofDiamonds, etc not shown.
column1 = Column(id: 0, cards: [
Card.ten.ofDiamonds,
Card.nine.ofSpades
])
column2 = Column(id: 1, cards: [
Card.king.ofSpades,
Card.queen.ofDiamonds,
Card.jack.ofClubs,
Card.ten.ofHearts
])
}
func moveCard() {
let card = column1.pop()
column2.push(card)
}
}
struct BoardView: View {
let board = Board()
var body: some View {
ZStack(alignment: .center) {
Rectangle()
.foregroundColor(.green)
.frame(maxWidth: .infinity, maxHeight: .infinity)
HStack(spacing: 20) {
ColumnView(column: board.column1)
ColumnView(column: board.column2)
}
Button(action: {
withAnimation {
self.board.moveCard()
}
}) {
Text("Move Card")
.font(.system(size: 20, weight: .bold, design: .default))
.foregroundColor(.white)
}
.offset(x: 0, y: 300)
}.edgesIgnoringSafeArea(.all)
}
}
有意义为什么会出现淡入/淡入-从第一个ColumnView
的角度来看,column1
丢了一张卡片(该卡片已从column1
数组),因此它会随着淡出而过渡;从第二个ColumnView
的角度来看,column2
获得了一张卡片(卡片已添加到column2
阵列中),因此它以淡入的方式过渡。
问题是,有没有一种方法可以实现我想要的功能,而不必完全重新构建已建立的数据模型和视图层次结构,并且Card
内有Column
个,并且CardView
内的ColumnView
个?保持数据模型的原样确实有帮助,将数据模型直接映射到视图层次结构也很有帮助。
我可以想象某种替代视图层次结构,其中只有52个CardView
,并根据其所在的列或位置对位置进行了某种修饰,但这确实很棘手。我考虑过某种定制的transition
,但是过渡是针对要插入或删除的视图的。有没有办法告诉SwiftUI Card
结构不是被删除并重新添加,而是被移动,从而执行匹配的移动动画?
答案 0 :(得分:0)
对于您的情况,我认为您需要为此行为计算每张卡相对于某些@State var
(或更多变量)的偏移量。我拿了您的代码并更改了BoardView
。当您点击按钮时,每个卡偏移量都会更改。这只是显示卡如何移动到新位置并可能为您提供正确方向的示例:
struct BoardView: View {
let board = Board()
@State var cards = [
Card.ten.ofDiamonds,
Card.nine.ofSpades,
Card.king.ofSpades,
Card.queen.ofDiamonds,
Card.jack.ofClubs,
Card.ten.ofHearts
]
@State var column1: Int = 2
var body: some View {
ZStack(alignment: .center) {
Rectangle()
.foregroundColor(.green)
.frame(maxWidth: .infinity, maxHeight: .infinity)
ZStack {
ForEach(0..<self.cards.count, id: \.self) { cardIndex in
CardView(card: self.cards[cardIndex])
.offset(x: cardIndex < self.column1 ? 0 : 135,
y: 35*CGFloat(cardIndex >= self.column1 ? cardIndex - self.column1 : cardIndex))
.frame(width: 125, height: 187)
}
}.offset(x: -80, y: 0)
Button(action: {
withAnimation {
self.column1 -= 1
}
}) {
Text("Move Card")
.font(.system(size: 20, weight: .bold, design: .default))
.foregroundColor(.white)
}
.offset(x: 0, y: 300)
}.edgesIgnoringSafeArea(.all)
}
}
所以也许正确的解决方案是将卡从一个阵列移动到另一个阵列,而不改变其偏移量。
答案 1 :(得分:0)
(从对问题的评论中复制)
从Xcode 11.3开始,iOS 13.3:
可悲的是,我向Apple提交了DTS票证,他们说目前无法在SwiftUI中完成我想做的事情。最后,我将“所有52张卡片”数组公开为计算属性,并使用锚点首选项和偏移量设置了卡片的位置以使其具有动画效果。 (换句话说,本质上是我希望避免的解决方案。)希望可以在iOS 14 / Xcode 12中对其进行改进。