I am using a UIView.transition to flip a card over.
While troubleshooting it not working, I stumbled upon a way to make it work - but I have no idea why. I am hoping that someone can look at the two code blocks below and help me understand why one works and the other doesn't. It seems very strange to me.
First, here is the code block that actually works. The card flip is visually flawless.
UIView.animate(withDuration: 0.01) { imageView.alpha = 1.0 imageView.layoutIfNeeded() // Works with and without this layoutIfNeeded() } completion: { (true) in UIView.transition(with: imageView, duration: 1.2, options: animation) { imageView.image = endingImage imageView.layoutIfNeeded() // Works with and without this layoutIfNeeded() } completion: { (true) in if self.dealTicketState.isTicketFaceUp == true { self.faceDownView.alpha = 0.0 } else { self.faceDownView.alpha = 1.0 } UIView.animate(withDuration: 0.01) { self.coveringLabel.backgroundColor = .clear self.coveringLabel.layoutIfNeeded() imageView.removeFromSuperview() self.tickNumLabel.alpha = originalTicketNumAlpha } } }
But I don't understand why I seem to need to wrap the UIView.transition() into the completion handler of a call to UIView.animate() in order for the flip animation to work.
*(Note: If I pull the "imageView.alpha = 1.0" out of the animate() block and place it immediately BEFORE calling UIView.animate() - the flip animation does not occur (with or without the layoutIfNeeded() call). It just toggles the images. *
Now, here is the code that I expected to work - but when I use this code instead of the above code, there is no "flip" transition. The card image just immediately toggles between the face up and face down image. The "UIView.transition" call here is identical to the one in the above code. The only difference here is that it's NOT wrapped into a 0.01 second UIView.animate completion block.
imageView.alpha = 1.0 imageView.layoutIfNeeded() UIView.transition(with: imageView, duration: 1.2, options: animation) { imageView.image = endingImage imageView.layoutIfNeeded() // same behaviour with and without this line } completion: { (true) in if self.dealTicketState.isTicketFaceUp == true { self.faceDownView.alpha = 0.0 } else { self.faceDownView.alpha = 1.0 } UIView.animate(withDuration: 0.01) { self.coveringLabel.backgroundColor = .clear self.coveringLabel.layoutIfNeeded() imageView.removeFromSuperview() self.tickNumLabel.alpha = originalTicketNumAlpha } }
This transition ends my flipTicket() function. The code that precedes this transition is identical in both cases. I wasn't going to include it because I don't think it's necessary to understand the problem - but then again - what do I know? Here's the stuff that came before the above clips:
func flipTicket() { let originalTicketNumAlpha = self.tickNumLabel.alpha self.tickNumLabel.alpha = 0.0 let tempFaceDownImage:UIImage = self.dealTicketState.faceDownImage let tempFaceUpImage:UIImage = getCurrentFaceUpImage() var endingImage:UIImage = self.dealTicketState.faceDownImage let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.contentMode = .scaleToFill imageView.clipsToBounds = true imageView.alpha = 0.0 self.coveringLabel.alpha = 1.0 self.coveringLabel.backgroundColor = .black self.coveringLabel.layoutIfNeeded() var animation:UIView.AnimationOptions = .transitionFlipFromLeft if faceDownView.alpha == 1.0 { animation = .transitionFlipFromRight imageView.image = tempFaceDownImage endingImage = tempFaceUpImage } else { animation = .transitionFlipFromLeft imageView.image = tempFaceUpImage } self.addSubview(imageView) NSLayoutConstraint.activate([ imageView.topAnchor.constraint(equalTo: self.topAnchor), imageView.bottomAnchor.constraint(equalTo: self.bottomAnchor), imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor), imageView.trailingAnchor.constraint(equalTo: self.trailingAnchor), ]) imageView.layoutIfNeeded()
Background: This code is part of a custom UI control which represents a playing card. It consists of several subviews. The topmost subview was originally a UIImageView which held the image of the back of the card. This let me simply toggle the alpha for that top view to display the card as either face up or face down. And then I added one more topMost view to the control - a UILabel with "background = .clear" and attached a UITapGestureRecognizer to it. When the control is tapped, this function which is meant to animate the flipping over of the card is called.
To construct the animation, I call the getCurrentFaceUp() function which temporarily sets the alpha of the card's faceDownView to 0 (so I can take a snapshot of the card underneath it as it is currently configured). It returns a UIImage of the "face up" view of the card. I already have a UIImage of the faceDown view. These are the 2 images I need for the transition.
So...then I set the background color of that topMost UILabel to .black, create a new temporary UIImageView and place it on top of the existing control. I set the temporary imageView to initially display whichever one of the 2 images is currently visible on the control. And then I run the flip transition, change the configuration of the background control to match the new state, change the label background back to .clear and dispose of the temporary UIImageView.
(If there's a better way to accomplish this, I'm open to hearing it but the main purpose of this post is to understand why my code appears to be acting strangely.)
When I was looking for a way to animate a card flip, I found a YouTube video that demonstrated the UIView.transition() with the flip animation. It did not require using a UIView.animate() wrapper in order to make it work - so I'm pretty certain that it's me that did something wrong -but I've spent hours testing variations and searching for someone else with this problem and I haven't been able to find the answer.
Thanks very much in advance to anybody that can help me understand what's going on here...
A little tough to tell (didn't try to actually run your code), but I think you may be doing more than you need to.
Take a look at this...
We'll start with two "Card View" subclasses - Front and Back (we'll "style" them in the next step):
Then, a "Playing Card View" class, that contains a "Front" view (cyan) and a "Back" view (red). On init, we add the subviews and set the "Front" view hidden. On tap, we'll run the flip transition between the Front and Back views:
and then here's a simple Controller example:
The result:
So, next step, we'll add a little styling to the Front and Back views -- no changes to the
PlayingCardViewfunctionality... just a couple new lines to set the styling...Card Front View - with rounded corners, a border and labels at the corners and center:
Looks like this:
Card Back View - with rounded corners, a border and a cross-hatch pattern:
Looks like this:
Playing Card View:
and finally, the same Controller example:
and here's the new result: