To help get you going...
First, no idea why you have your "deckPileImage" in a stack view, but assuming you have a reason for doing so...
a simple "card" view - bordered with rounded corners
class CardView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
layer.cornerRadius = 16
layer.masksToBounds = true
layer.borderWidth = 1
layer.borderColor = UIColor.black.cgColor
}
}
a basic view controller - adds a "deck pile view" to a stack view, and a "card position view" as the destination for the new, animated cards.
class AnimCardVC: UIViewController {
let deckStackView: UIStackView = UIStackView()
let cardPositionView: UIView = UIView()
let deckPileView: CardView = CardView()
let cardSize: CGSize = CGSize(width: 80, height: 120)
// card colors to cycle through
let colors: [UIColor] = [
.systemRed, .systemGreen, .systemBlue,
.systemCyan, .systemOrange,
]
var colorIDX: Int = 0
// card position constraints to animate
var animXAnchor: NSLayoutConstraint!
var animYAnchor: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
deckStackView.translatesAutoresizingMaskIntoConstraints = false
deckPileView.translatesAutoresizingMaskIntoConstraints = false
cardPositionView.translatesAutoresizingMaskIntoConstraints = false
deckStackView.addArrangedSubview(deckPileView)
view.addSubview(deckStackView)
view.addSubview(cardPositionView)
// always respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
deckStackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
deckStackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
// we'll let the stack view subviews determine its size
deckPileView.widthAnchor.constraint(equalToConstant: cardSize.width),
deckPileView.heightAnchor.constraint(equalToConstant: cardSize.height),
cardPositionView.topAnchor.constraint(equalTo: deckStackView.bottomAnchor, constant: 100.0),
cardPositionView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
cardPositionView.widthAnchor.constraint(equalToConstant: cardSize.width + 2.0),
cardPositionView.heightAnchor.constraint(equalToConstant: cardSize.height + 2.0),
])
// outline the card holder view
cardPositionView.backgroundColor = .systemYellow
cardPositionView.layer.borderColor = UIColor.blue.cgColor
cardPositionView.layer.borderWidth = 2
// make the "deck card" gray to represent the deck
deckPileView.backgroundColor = .lightGray
}
func animCard() {
let card = CardView()
card.backgroundColor = colors[colorIDX % colors.count]
colorIDX += 1
card.translatesAutoresizingMaskIntoConstraints = false
card.widthAnchor.constraint(equalToConstant: cardSize.width).isActive = true
card.heightAnchor.constraint(equalToConstant: cardSize.height).isActive = true
view.addSubview(card)
// center the new card on the deckCard
animXAnchor = card.centerXAnchor.constraint(equalTo: deckPileView.centerXAnchor)
animYAnchor = card.centerYAnchor.constraint(equalTo: deckPileView.centerYAnchor)
// activate those constraints
animXAnchor.isActive = true
animYAnchor.isActive = true
// run the animation *after* the card has been placed at its starting position
DispatchQueue.main.async {
// de-activate the current constraints
self.animXAnchor.isActive = false
self.animYAnchor.isActive = false
// center the new card on the cardPositionView
self.animXAnchor = card.centerXAnchor.constraint(equalTo: self.cardPositionView.centerXAnchor)
self.animYAnchor = card.centerYAnchor.constraint(equalTo: self.cardPositionView.centerYAnchor)
// re-activate those constraints
self.animXAnchor.isActive = true
self.animYAnchor.isActive = true
// 1/2 second animation
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
})
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
animCard()
}
}
It looks like this:

Each time you tap anywhere the code will add a new "card" and animate it from the "deck" view to the "card position" view.
