skip to Main Content

Hello I am new at swift and IOS apps , I am trying to animate movement of cardBackImage (UIImage) from deckPileImage to the card view, but everything got different superViews and I have no idea how to do it properly , all the location have different frames ( superviews as described in the Image) , Should I use CGAffineTransform ?

viewHierarchyDescription

try to imagine my abstraction as a "face down card fly from deck into its possition on boardView"

2

Answers


  1. 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:

    enter image description here

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

    enter image description here enter image description here enter image description here

    Login or Signup to reply.
  2. Don’t animate the view at all. Instead, animate a snapshot view as a proxy. You can see me doing it here, in this scene from one of my apps.

    enter image description here

    That red rectangle looks like it’s magically flying out of one view hierarchy into another. But it isn’t. In reality there are two red rectangles. I hide the first rectangle and show the snapshot view in its place, animate the snapshot view to where the other rectangle is lurking hidden, then hide the snapshot and show the other rectangle.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search