UIView.transition animation causing view to become nil - ios

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?

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'

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!

Related

Making an animation to slide and hide/show an UIView(swift 5)

I want to add animation to these two views.
Red UIView
Green UIView
My storyboard look like this
From the picture I want to add an animation when click on these two views.
First start with hide red UIView.
Action : 1
when i click on green view i want green uiview silde to the right side until it disappear
and the red UIView will slide out from the right side immediately.
red uiview slide from right side
and stopp when it is at that point in the storyboard and hide green UIView.
Action : 2
and when i click on red view i want it to slide right until it disappears. Show green UIView and comes out from the right corner as well and hide red UIView.
red UIView slide out
My Code
import UIKit
class TestViewCell: UICollectionViewCell {
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var bgAlertView: UIView!
#IBOutlet weak var imgAlert: UIImageView!
#IBOutlet weak var bgAlreadyAlertView: UIView!
#IBOutlet weak var imgAlreadyAlert: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
//Make an action when tap on bgAlertView
let actionBgAlert : Selector = #selector(self.actionBgAlert)
let viewPostsViewGesture = UITapGestureRecognizer(target: self, action: actionBgAlert)
bgAlertView.isUserInteractionEnabled = true
bgAlertView.addGestureRecognizer(viewPostsViewGesture)
//Make an action when tap on bgAlreadyAlertView
let actionBgAlreadyAlert : Selector = #selector(self.actionBgAlreadyAlert)
let viewAlreadyViewGesture = UITapGestureRecognizer(target: self, action: actionBgAlreadyAlert)
bgAlreadyAlertView.isUserInteractionEnabled = true
bgAlreadyAlertView.addGestureRecognizer(viewAlreadyViewGesture)
}
//action1
#objc func actionBgAlert(sender: UITapGestureRecognizer){
if imgAlert.image == #imageLiteral(resourceName: "alarm") {
self.bgAlertView.isHidden = true
self.bgAlreadyAlertView.isHidden = false
}
//action2
#objc func actionBgAlreadyAlert(sender: UITapGestureRecognizer){
if imgAlreadyAlert.image == #imageLiteral(resourceName: "alarmedMain") {
self.bgAlertView.isHidden = false
self.bgAlreadyAlertView.isHidden = true
}
}
In the storyboard set constraints on the size of the views. Set constraints from the right side of the red view and green view from the right side of the superview they share. Define some constants for the values needed for both positions for both views.
Then something like this:
#IBOutlet weak var greenConstraint : NSLayoutConstraint
#IBOutlet weak var redConstraint : NSLayoutConstraint
let greenSlideOutValue = -2000.0 // big enough to get green view offscreen
let redSlideInValue = 0.0 // aligns red view right edge to superview
let greenSlideInValue = 100.0 // puts green view onscreen offset from right edge
let redSlideOutValue = -2500.0 // big enough to get red view offscreen.
UIView.animate(withDuration: 0.75, delay: 1.0, options: .curveEaseOut, animations: {
greenConstraint.constant = greenSlideOutValue
redConstraint.constant = redSlideInValue
self.view.layoutIfNeeded()
}, completion: { finished in
print(“slide green out, red in”)
})
Do this in the event handler for say a tap or gesture recognizer associated with the views. Do similar for the red view event(s)
Code isn’t compiled and is just typed in but should get you started.

IBOutlet is nil after flip transition animation

My two IBoutlet of two views are there which i am trying to flip. After UIView.transition, "from" IBOutlet sets to nil, unable to figure out the reason behind this.
#IBOutlet weak var showingSideView: shadowView!
#IBOutlet weak var hiddenSideView: shadowView!
UIView.transition(from: showingSideView, to: hiddenSideView, duration: 0.7, options: transitionDirection) { (isFlipped) in
self.showingBack = !self.showingBack
}
That is because your view in "from" will be removed from its superview after the transition finishes.
Read more in the docs
As you defined your outlet as weak, there is no retaining object anymore so it gets deallocated.
If you read through the documentation :
By default, the view in fromView is replaced in the view hierarchy by the view in toView. If both views are already part of your view hierarchy, you can include the showHideTransitionViews option in the options parameter to simply hide or show them.
So, to keep your views in the hierarchy, add that option:
#IBOutlet var showingSideView: shadowView!
#IBOutlet var hiddenSideView: shadowView!
UIView.transition(from: showingSideView,
to: hiddenSideView,
duration: 0.7,
options: [.transitionFlipFromLeft, .showHideTransitionViews])
{ (isFlipped) in
self.showingBack = !self.showingBack
}
Edit:
Just for clarification...
Apple now recommends NOT using weak for #IBOutlets, but that has no effect on the problem here. When using .showHideTransitionViews the reference will not become nil even if the #IBOutlets are set to weak.

Can't animate a position change of two container UIViews that have child view controllers

I have two views in self.view that are container views (via storyboard: each has an embed segue in viewDidLoad which loads up their respective child view controller). They're called self.leftPane and self.rightPane. I'm using auto-layout.
I want to animate a position change of self.leftPane, as it moves from x=0 to x=(negative something). I'm basically trying to make a hamburger menu on the left, in a simple fashion...
#IBOutlet weak var leftPane: UIView! // has child view controller embedded
#IBOutlet weak var rightPane: UIView! // has child view controller embedded
// in/after viewDidAppear
self.leftPaneLeftConstraint.constant = 0 - self.leftPane.frame.size.width + 20.0
UIView.transition(with: self.leftPane, duration: 1.0, options: [], animations: {
self.leftPane.layoutIfNeeded()
}, completion: { (finished) in
self.leftPaneIsFull = false
})
The problem is that there's no animation -- it just jumps. I've tried UIView.transition with different views, and different UIView.animate flavors, all with the same effect (no actual animation).
What am I missing? Thanks!
You should use UIView.animate instead of UIView.transition.I wrote down the code sample.Are you try this code?
#IBOutlet weak var leftPane: UIView! // has child view controller
embedded
#IBOutlet weak var rightPane: UIView! // has child view controller embedded
// in/after viewDidAppear
UIView.animate(withDuration: 1.0, animations: {
self.leftPaneLeftConstraint.constant = 0 - self.leftPane.frame.size.width + 20.0
self.view.layoutIfNeeded()
}, completion: { (success) -> Void in
//You can write something you want
})
}
})
Enjoy it :)

Swift: transitionFromView flip whole screen instead of view

I have a weird issue with swift. I am trying to flip two views, not the whole screen but it flips the whole screen.
It has been asked before on here but no answer:
flip animation in swift flips whole view not subviews
This is the references to the views I have (I have confirmed they only take up part of the screen):
#IBOutlet var frontview: UIView!
#IBOutlet var backview: UIView!
And this is my code for flipping:
func flipCard() {
if (showingBack) {
UIView.transitionFromView(backview, toView: frontview, duration: 1, options: UIViewAnimationOptions.TransitionFlipFromRight, completion: nil)
showingBack = false
} else {
UIView.transitionFromView(frontview, toView: backview, duration: 1, options: UIViewAnimationOptions.TransitionFlipFromLeft, completion: nil)
showingBack = true
}
}
Thanks for your help
What happens when you call UIView.transitionFromView(backView, toView: frontView ... is it flips the parent of those two views. Presumably the parent in your case is the root view of the ViewController, so to get it to flip just those views instead of the "whole screen" you would just need to embed both backView and frontView in another view.
There's more details in this article.
Try This example. I think you are looking something like this. instead of imageView try UIView in your case.

A view that animates upward without filling the screen

So this is what I am trying to do:
Here is the initial screen:
When the bottom pointing arrow is clicked there is a smaller view that transitions from bottom to top like this:
And as you can see the view in the back is dimmed. And when I click on the back view, the smaller sized view goes away by animating downwards.
There were several things that I tried:
1. I tried to segue modally which seemed to animate properly, namely, from bottom to top, but it covers the entire back view.
2. I tried to make the modal view only half the parent size by trying to replicate this post: Present modal view controller in half size parent controller. However, it did not work.
3. So I decided to put a UIView on top of my back view like so:
And I connected the grey colored view with the #IBOutlet weak var messageView: UIView!. And I tried using this code: UIView.transitionWithView(messageView, duration: 1.0, options: UIViewAnimationOptions.CurveEaseIn, animations: nil, completion: nil). However, nothing seems to be happening. Any suggestions on how to accomplish this?
For the Animation of button:
Add autolayout constraint to the BottomLayout Guide.And create an IBOutlet as
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
var shouldAnimateView:Bool = true
On the Action of the button you need to animate the View using constraint
#IBAction func showOrHideViewBtn(sender: AnyObject) {
if shouldAnimateView {
self.bottomConstraint.constant = self.view.frame.size.height/2+5
UIView.animateWithDuration(Double(0.2), animations: {
self.bottomConstraint.constant = self.view.frame.size.height/2 - self.toanimateView.frame.size.height
self.view.layoutIfNeeded()
})
}else{
self.bottomConstraint.constant = self.view.frame.size.height/2 - self.toanimateView.frame.size.height
UIView.animateWithDuration(Double(0.2), animations: {
self.bottomConstraint.constant = self.view.frame.size.height/2+5
self.view.layoutIfNeeded()
})
}
shouldAnimateView = !shouldAnimateView
}

Resources