Ios SwiftUI:为视图从一个堆栈到另一个堆栈的移动设置动画? 背景

Ios SwiftUI:为视图从一个堆栈到另一个堆栈的移动设置动画? 背景,ios,swiftui,Ios,Swiftui,使用SwiftUI,我正在开发一款类似纸牌的纸牌游戏,其中包括堆叠扑克牌和将扑克牌从一个堆叠移动到另一个堆叠的功能,例如: 在这里的示例中,当我点击“移动卡片”按钮时♠️9从列1移动到列2,以坐在❤️10,动画是♠️9从第一列淡出,而新的♠️9同时淡入第二列 我想要的是♠️9“查看自身”以直接从其原始位置设置动画到其在屏幕顶部的最终位置❤️十, (在我的实际应用程序中,卡片也可以放在其他位置,但实现方式类似于ColumnView) 代码 我有一个由列类组成的数据模型,该类包含卡对象数组,以及相

使用SwiftUI,我正在开发一款类似纸牌的纸牌游戏,其中包括堆叠扑克牌和将扑克牌从一个堆叠移动到另一个堆叠的功能,例如:

在这里的示例中,当我点击“移动卡片”按钮时♠️9从列1移动到列2,以坐在❤️10,动画是♠️9从第一列淡出,而新的♠️9同时淡入第二列

我想要的是♠️9“查看自身”以直接从其原始位置设置动画到其在屏幕顶部的最终位置❤️十,

(在我的实际应用程序中,卡片也可以放在其他位置,但实现方式类似于
ColumnView

代码 我有一个由
类组成的数据模型,该类包含
对象数组,以及相应的
列视图
卡视图
结构:

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)"
    }
}

最后是一个
Board
对象,它包装
对象,并显示相应的
BoardView
(本例中的主屏幕):

问题: 淡出/淡入的原因很有道理-从第一个
ColumnView
的角度来看,
column1
丢失了一张卡(卡已从
column1
阵列中移除),因此它会淡出;从第二个
ColumnView
的角度来看,
column2
获得了一张卡(卡被添加到
column2
数组中),因此它以淡入的方式过渡

问题是,有没有一种方法可以实现我想要的,而不必完全重新构建我所设置的数据模型和视图层次结构,即
Card
s在
Column
s中,而
CardView
s在
ColumnView
s中?以这种方式建立数据模型确实很有帮助,而且将数据模型直接映射到视图层次结构也很有帮助


我可以想象某种备用视图层次结构,其中只有52个
cardwiew
s,并根据它们所在的列或位置对位置进行某种修改,但这感觉非常粗糙。我考虑过可能是某种自定义的
转换
,但是转换是用于插入或删除视图的。有没有办法告诉SwiftUI,
卡的结构不是被移除和重新添加的,而是被移动的,因此要执行匹配的移动动画?

我认为对于您的情况,您需要计算每个卡的偏移量,从这个行为的一些
@State var
(或更多变量)开始。我获取了您的代码并更改了
BoardView
。当您点击按钮时,每张卡的偏移量都会发生变化。这是唯一一个显示卡如何移动到新位置并可能为您提供正确方向的示例:

struct BoardView:View{
让board=board()
@状态变量卡=[
十张钻石卡,
第九张牌,
卡德·奥夫帕德斯国王,
钻石女王卡,
卡德杰克俱乐部,
十心牌
]
@状态变量column1:Int=2
var body:一些观点{
ZStack(对齐:。中心){
矩形()
.foregroundColor(.绿色)
.frame(maxWidth:无穷大,maxHeight:无穷大)
ZStack{
ForEach(0..=self.column1?cardIndex-self.column1:cardIndex))
.框架(宽:125,高:187)
}
}.偏移量(x:-80,y:0)
按钮(操作:{
动画片{
self.column1-=1
}
}) {
文本(“移动卡”)
.font(.system(大小:20,重量:粗体,设计:默认值))
.foregroundColor(.白色)
}
.偏移量(x:0,y:300)
}.edgesIgnoringSafeArea(.all)
}
}
因此,也许正确的解决方案是将卡从一个阵列移动到另一个阵列,而不改变其偏移量。

(摘自对该问题的评论)

从Xcode 11.3、iOS 13.3开始:


遗憾的是,我向苹果提交了DTS申请单,他们说目前不可能在SwiftUI中实现我想做的事情。最后,我将“所有52张卡”数组作为计算属性公开,并使用锚点首选项和偏移设置卡的位置,以允许它们设置动画。(换句话说,基本上是我希望避免的解决方案。)希望这在iOS 14/Xcode 12中得到改进。

谢谢-这就是我的意思“我可以想象某种替代视图层次结构,其中只有52个CardView,并根据它们所在的列或位置对位置进行某种修改,但这感觉非常粗糙。“仅仅在一个巨大的视图中放置52张卡片而没有组织,感觉不是很迅速。另外,我的实际使用就像纸牌游戏一样,纸牌的位置可能比列中的位置更多-纸牌也可以在其他位置-因此,即使只是使用偏移量单独处理列也不会有太大帮助。我很想知道除了下面的答案之外,您是否找到了其他相关信息!遗憾的是,我向苹果提交了一份DTS申请单,他们说目前不可能在SwiftUI上实现我想做的事情。最后,我将“所有52张卡”数组作为计算属性公开,并使用锚点首选项和偏移设置卡的位置,以允许它们设置动画。(换句话说,基本上是我希望避免的解决方案。)该死。。。谢谢你让我知道,很高兴听到苹果的确认(建议你自己用这个答案回答这个问题,并可能指定当前的Xcode版本,因为希望将来它会改变!)我想知道是否有可能实现你在UIKit中可能做的事情,以及当一张卡出现时
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
    }
}
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)
    }
}