Robert Brax
Robert Brax

Reputation: 7318

UIView.transition animation causing view to become nil

Goal of code is that on each tap of a view containing 2 UIImageview, to have the bottom image go on top of the to image, and so on each time I tap.

I have a view container with 2 UIImageview on top of each other:

@IBOutlet weak var imagesContainer: UIView!
@IBOutlet weak var imageZero: UIImageView!
@IBOutlet weak var imageOne: UIImageView!

I add a tap gesture in ViewDidLoad:

 let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapImagesContainer(_:)))
 self.imagesContainer.addGestureRecognizer(tapGestureRecognizer

Also a top level variable, to control which image to bring on top, and which to push down:

      var imageOnTopIsImageZero = true

so that when I tap on the container, the under image comes on top. While developing I implemented a transition without animation:

@objc func didTapImagesContainer(_ sender: UITapGestureRecognizer) {
    let imageToBringOnTop: UIImageView? = imageOnTopIsImageZero ? self.imageOne : self.imageZero
    let ImagetoBringDown: UIImageView? = imageOnTopIsImageZero ? self.imageZero : self.imageOne
    
    imageToBringOnTop?.layer.zPosition = 1.0
    ImagetoBringDown?.layer.zPosition = 0.0
    self.imageOnTopIsImageZero.toggle()
}

This works fine. Now I tried to implement the same transition with animation:

@objc func didTapImagesContainer(_ sender: UITapGestureRecognizer) {
    let imageToBringOnTop: UIImageView? = imageOnTopIsImageZero ? self.imageOne : self.imageZero
    let ImagetoBringDown: UIImageView? = imageOnTopIsImageZero ? self.imageZero : self.imageOne
    
    UIView.transition(from: ImagetoBringDown!, to: imageToBringOnTop!, duration: 1.0, options: .transitionCrossDissolve, completion: nil)
    self.imageOnTopIsImageZero.toggle()
}

The first time I tap on the image, the transition happens correctly, underimage is cross disolved into the on top image. But on the second tap, imageToBringOnTop is nil!

I really don't understand why the animation has an effect on the view content. What is the cause, and how to resolve this issue?

Upvotes: 0

Views: 360

Answers (2)

Shadowrun
Shadowrun

Reputation: 3857

The docs say: fromView The starting view for the transition. By default, this view is removed from its superview as part of the transition.

Detail: The idea of a transition animation is that you're changing the view hierarchy by replacing one view with another in an animated way. If you are not doing such a 'transition', you can use other (non-transition) animation API like animateWithDuration:delay:options:animations:completion:

Or there is support for keeping two views in the hierarchy, one shown, one hidden, using the transition animation API, if you include UIViewAnimationOptionShowHideTransitionViews in the option set:

UIView.transition(from: ImagetoBringDown!, to: imageToBringOnTop!,
   duration: 1.0,
   options: [.transitionCrossDissolve, .showHideTransitionViews],
   completion: nil) 

So "showHideTransitionViews" means 'show/hide the from/to instead of add/remove'

Upvotes: 1

Sweeper
Sweeper

Reputation: 270890

Rather than setting the z positions, transition actually removes the from view from the view hierarchy, and adds the to view to the view hierarchy (documentation):

Parameters

fromView

The starting view for the transition. By default, this view is removed from its superview as part of the transition.

toView The ending view for the transition. By default, this view is added to the superview of fromView as part of the transition.

Also note that since your VC is holding a weak reference to the images, their superviews are the only objects holding a strong reference to them. Once one of them is removed from its superview, your VC's weak reference becomes nil.

To fix this, simply use strong references, remove the word weak:

@IBOutlet var imageZero: UIImageView!
@IBOutlet var imageOne: UIImageView!

Upvotes: 0

Related Questions