Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS - Card flip animation

I have two UIImageViews. One is the 'Front' and the other is the 'Back'. I'm trying to implement it so that when you click onto the 'Back' it will trigger the animation and flip the card.

The animation works perfectly. But it animates the full page, which I don't want. I only want the UIImageView to flip. I can't see what I'm doing wrong. Probably something obvious.

@IBOutlet weak var answerImageViewer: UIImageView?
@IBOutlet weak var backAnswerImageViewer: UIImageView?

override func viewDidLoad() {
    super.viewDidLoad()
    startNewGame()
    retrieveNewQuestion()
    setupBannerMessage()
    customPlayButtons()

    let tapGesture = UITapGestureRecognizer(target: self, action: Selector("tap"))
    tapGesture.numberOfTapsRequired = 1
    backAnswerImageViewer?.addGestureRecognizer(tapGesture)
    backAnswerImageViewer?.userInteractionEnabled = true
}

var showingBack = true

func tap (){
    if showingBack{
       UIView.transitionFromView(self.backAnswerImageViewer!, toView: self.answerImageViewer!, duration: 1, options: UIViewAnimationOptions.TransitionFlipFromRight, completion: nil)

        backAnswerImageViewer?.hidden = true
        answerImageViewer?.hidden = false

        showingBack = false
    }
    else {
        showingBack = true

        UIView.transitionFromView(self.answerImageViewer!, toView: self.backAnswerImageViewer!, duration: 1, options: UIViewAnimationOptions.TransitionFlipFromRight, completion: nil)

        backAnswerImageViewer?.hidden = false
        answerImageViewer?.hidden = true
    }
}
like image 391
Scott B Avatar asked Sep 15 '16 19:09

Scott B


4 Answers

If you have a button that holds "card" as a variable, and you want to change his image to a new image called "1". All you have to do is this:

let image = UIImage(named: "1")
card.setImage(image, for: .normal)
UIView.transition(with: card, duration: 2, options: .transitionFlipFromRight, animations: nil, completion: nil)
like image 104
J. Doe Avatar answered Oct 18 '22 00:10

J. Doe


Your problem is that your UIImageViews are directly on the "full page" view.

transitionFromView removes the fromView from its superview and adds the toView on the superview with the given animation. Thus, it animates the superview.

You should include a UIView that servers as a container and have both imageViews as subviews. Add your tap gesture on the containerview. Also, you should not have weak references to the imageViews, since once you have done the animation once, your reference to the back imageView will be gone. It is probably better to add these in code rather than storyboard. No need to hide the imageViews.

Here are some sample code:

class MyViewController: UIViewController {

        @IBOutlet weak var containerView: UIView!

        private let backImageView: UIImageView! = UIImageView(image: UIImage(named: "back"))
        private let frontImageView: UIImageView! = UIImageView(image: UIImage(named: "front"))

        private var showingBack = false

        override func viewDidLoad() {
            super.viewDidLoad()

            frontImageView.contentMode = .ScaleAspectFit
            backImageView.contentMode = .ScaleAspectFit

            containerView.addSubview(frontImageView)
            frontImageView.translatesAutoresizingMaskIntoConstraints = false
            frontImageView.spanSuperview()

            let singleTap = UITapGestureRecognizer(target: self, action: #selector(flip))
            singleTap.numberOfTapsRequired = 1
            containerView.addGestureRecognizer(singleTap)
        }

        func flip() {   
            let toView = showingBack ? frontImageView : backImageView
            let fromView = showingBack ? backImageView : frontImageView
            UIView.transitionFromView(fromView, toView: toView, duration: 1, options: .TransitionFlipFromRight, completion: nil)
            toView.translatesAutoresizingMaskIntoConstraints = false
            toView.spanSuperview()
            showingBack = !showingBack
        }   

    }
like image 38
Carien van Zyl Avatar answered Oct 18 '22 01:10

Carien van Zyl


This solution is for flipping "a card", using two UIImageView (just like you are using...), I am using it inside a UICollectionView, that that is of course not neccessary.

class CardCVCell: UICollectionViewCell {
    @IBOutlet weak var cardFrontImageView: UIImageView!
    @IBOutlet weak var cardBackImageView: UIImageView!

    private var flipped: Bool = false {
        didSet {
            cardFrontImageView.visible = flipped
            cardBackImageView.hidden = flipped
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        cardBackImageView.backgroundColor = UIColor.brownColor()
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        cardFrontImageView.image = nil
        flipped = false
    }

    func flipCard(cardModel: Card) {
        let flipped = cardModel.flipped
        let fromView = flipped ? cardFrontImageView : cardBackImageView
        let toView = flipped ? cardBackImageView : cardFrontImageView
        let flipDirection: UIViewAnimationOptions = flipped ? .TransitionFlipFromRight : .TransitionFlipFromLeft
        let options: UIViewAnimationOptions = [flipDirection, .ShowHideTransitionViews]
        UIView.transitionFromView(fromView, toView: toView, duration: 0.6, options: options) {
            finished in
            cardModel.flipped = !flipped
        }
    }
}

The code is taken from my open source project introducing people to Swift development, called SwiftIntro which is a memory game, fetching images from Instagram.

Please observe that the model Card must be a class and not a value type

like image 7
Sajjon Avatar answered Oct 18 '22 01:10

Sajjon


You need to have a container view. Put answerImageViewer and backAnswerImageViewer into one single UIView. Here's my sample code.

    let frontView = UIView()
    let backView = UIView()

    let frame = CGRect(x: 40, y: 100, width: 300, height: 400)
    frontView.backgroundColor = .red
    backView.backgroundColor = .blue

    frontView.frame = CGRect(x: 0, y: 0, width: 300, height: 400)
    backView.frame = CGRect(x: 0, y: 0, width: 300, height: 400)

    let containerView = UIView()
    containerView.frame = frame

    containerView.addSubview(backView)
    containerView.addSubview(frontView)

    view.addSubview(containerView)
like image 3
wint Avatar answered Oct 18 '22 01:10

wint