When animating view with UIView.animate other views are also needlessly animated - ios

I'm wondering why views in my custom TableViewCells are affected by animation which should animate only 2 views in my floating pager (which is even not in TableView's view hierarchy..
UIView.animate(withDuration: 0.3) {
self.stateIndicator.isHiddenInStackView = true
self.loaderIndicator.isHiddenInStackView = false
}
When I comment out animation block everything stops animating.
Attached GIF describes everything (Blue views gets animated corner radius, badges 'flies in' instead of just appearing):
UIView.animate strange behavior
Any help or hint will be appreciated :).
Regards Tom.

Write the things which you don't want to animate in the block
UIView.performWithoutAnimation {
//Write your code here
}

I make my animation in main queue and it worked.
For example
DispatchQueue.main.async {
//Write animation code here
}

Related

First time animating is glitchy

I am animating a view gradientView in/out using the following:
func hideOrShowGradientView(hide: Bool) {
UIView.animate(withDuration: 0.4, animations: {
self.gradientView.isHidden = hide
})
}
This works well, but on the first time, there is no animation. It just appears. On the second and third time it works wonderfully. I've tried calling the animate block on the main thread but no luck there. Why is this animation failing to occur on the first and only first time around? Should I be using another animation method?
Have you tried calling self.view.layoutIfNeeded(). It forces the view and it's subviews to complete any of it's pending animations immediately, which might be interfering with your animation. You can use it like this:
func hideOrShowGradientView(hide: Bool) {
self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.4, animations: {
self.gradientView.isHidden = hide
self.view.layoutIfNeeded()
})
}
Apple recommends calling layoutIfNeeded() twice, once before the animation block which forces a redraw on the view and it's subviews, and it completes any pending animations on the view without waiting for the next update cycle, and call it the second time inside the animation block to make sure that the animation changes will be applied immediately.
I had too much going on the main thread in the init method, which was being called only on the first time the view was being presented. Moved unnecessary initialization into view did appear

UIStackView subview contents visible during hiding animation

I am using a UIStackView for my layout. In that stack view, when I press a button I want to hide one of the subviews. That subview contains a couple buttons and a label. My issue is that during the hide animation, the buttons and label are visible until the vertical space from the subview is fully animated away.
Is there something I can do so that when I call subview.isHidden = true, the subview`s contents hide immediately at the beginning of the animation instead of at the very end of the animation?
use a custom stackView class. Use IBOutlets in the class to reference the buttons/text and write a function that hides your outlets when self.isHidden = true. Let me know if you need more explanation.
Besides hiding the buttons and content view with an animation you could try changing the background color from clear on the views inside the stackview to the same color as the background on your view. This still might not look great but it would be better.
Obviously animation would be something like the code below but give background color on your content views in the stackview a shot.
UIView.animate(withDuration: 0.1, animations: {
//yourContentHoldingView.alpha = 0
})

Animate NSLayoutConstraint & view simultaneously in separate blocks

I have two relatively simple animations. One is animating the top constraint of a UIButton so that it slides up. The other is animating the background color on a UIView.
self.buttonAnimationConstant.constant = 0
self.view.layoutIfNeeded() // ensure the constraint is at 0
self.buttonAnimationConstraint.constant = 100
UIView.animateWithDuration(0.25, delay: 0.0) {
self.view.layoutIfNeeded()
}
and my color animation:
UIView.animateWithDuration(0.25, delay: 0.0) {
self.colorView.backgroundColor = UIColor.purpleColor()
}
If I try executing them at the same time, then the button will animate up but it cancels the color animation, presumably because of view.layoutIfNeeded. Note that these two animations are in separate places so they can't be joined into one block (one sits at the view controller level, the other embedded inside a custom view but both within the same view controller). How can I animate both a constraint and a view property such that one doesn't cancel the other?
Essentially the issue is how do you go about animating both a constraint and a view that sit in the same view hierarchy?
Animation blocks do not cancel each other. The issue here is this line:
self.view.layoutIfNeeded() // ensure the constraint is at 0
Calling layoutIfNeeded outside an animation block will cancel your animations (because they set a new value for the values you animate) and there is not much you can do to prevent that. What you could do is making sure that no layoutIfNeeded is called outside an animation block while performing an animation. Of course this does not happen if the views are not in the same view hierarchy.

stop animateWithDuration in navigationItem

i am trying to animate my navigationItem.rightBarButtonItem to blink repeatedly from green to white... So I do this
UIView.animateWithDuration(1.0, delay:0, options: [.Repeat, .Autoreverse], animations: {
self.navigationItem.rightBarButtonItem?.tintColor = .greenColor()
}, completion: nil)
as i see in this question
and it work
the problem is that i am using UITabView and every time i switch between views and return to the first view the blink is stop
and i think its because i stuck layer after layer of animation somewhere
i understand that to remove the animation i should use layer.removeAllAnimations()
the problem is that i don't know which view to do it on
i tried self.view.layer.removeAllAnimations()
and it didn't work.
how do i get to this layer that i create for the navigationItem animation
so i can remove it?
i solve this issue with relocating the creation of the animation
in the beginning i placed that in the viewWillAppear function
and also tried to init the button to white color just before that
but its looks like it didn't work that way so i relocate the animation to
the viewDidAppear function and leave the init of the button to white color
in the viewWillAppear and its seems to work ok now :)
thanks

How can I flip an iOS UITableViewCell?

I have a tableview with a number of cells. Each cell has a little info "i" button, which I would like the user to be able to click in order to edit the information in that cell, by "flipping" the cell from the right.
I have a separate table cell XIB for each of the two possible cell states, and cellForRowAtIndexPath provides the correct XIB (with a different cell identifier) depending on whether the cell is supposed to be in the regular view or the detail/edit view. This works fine using, e.g., reloadRowsAtIndexPaths to reflect the changed state when the "i" button is pressed.
But I just can't figure out how to animate a flip when the cell's "i" button is pressed. Flipping isn't one of the options for "withRowAnimation" in reloadRowsAtIndexPaths. And using a standard animation approach like UIView animateWithDuration doesn't work: reloadRowsAtIndexPaths just causes the relevant cell to reload instantly, ignoring the animation instructions. I even tried to use drawViewHierarchyInRect to pick up the cell's future state as an image, but I couldn't get the cell to redraw itself quickly enough to be picked up by drawViewHierarchyInRect (and I don't want the user to actually see the result of the animation before the animation).
Does anyone have an idea of how to handle this?
EDIT
To summarize, the challenge I'm having is: is there another way to swap UITableView cell contents with a different UITableViewCell Xib besides reloadRowsAtIndexPaths (which breaks animations), without causing trouble with the IBOutlets of the new Xib? (Loading it as a Nib into a subview seems to break the IBOutlets.) The answers so far haven't addressed this.
It will be complicated if u use two subclassed UITableViewCell, instead use a single subclassed UITableViewCell, and perform flip-animation for example,
let xib contains the subclass UITableViewCell in its contentView add two views
normal View
Flipped View
in this view's add the contents what u want to display, and make sure both hav outlet in the cell
for example that i took
in the above two view first one View-FlipView and its contents label and button and second one is also same as first view, top view should be the normalView
connect the button actions to cell and u just perform like below in custom cell
//flip the view to flipView
- (IBAction)flipButtonAction:(UIButton *)sender
{
[UIView transitionWithView:self.contentView duration:0.6 options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
[self.contentView insertSubview:_flipView aboveSubview:_normalView];
} completion:^(BOOL finished) {
}];
}
//flip the view back to normalView
- (IBAction)flipBackButtonAction:(id)sender
{
[UIView transitionWithView:self.contentView duration:0.6 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
[self.contentView insertSubview:_normalView aboveSubview:_flipView];
} completion:^(BOOL finished) {
}];
}
and it will look like something below
any problem just comment ... :) hope this helps u .. :)
Swift 4:
#IBAction func flipButtonAction(_ sender: UIButton) {
UIView.transition(with: contentView, duration: 0.6, options: .transitionFlipFromRight, animations: {() -> Void in
self.contentView.insertSubview(flipView, aboveSubview: normalView)
}, completion: {(_ finished: Bool) -> Void in
})
}
//flip the view back to normalView
#IBAction func flipBackButtonAction(_ sender: Any) {
UIView.transition(with: contentView, duration: 0.6, options: .transitionFlipFromLeft, animations: {() -> Void in
self.contentView.insertSubview(normalView, aboveSubview: flipView)
}, completion: {(_ finished: Bool) -> Void in
})
}
You probably don't want to reload the table's data to flip any given cell. Your cells should be able to flip "on their own." So you'll need to subclass UITableViewCell and in your subclass you'll have something like a flip function, that does some animations of the cell's subviews.
Now here's a trick with flip animations. You can't just change the view hierarchy and flip at the same time, or you'll see the "back side" of your view at the wrong time during the animation. Use two animations in a row:
[UIView animateWithDuration:... {
// Flip the content "half way" by rotating 90 degrees about the y axis.
// At the end of this animation your view will be perpendicular to the
// screen and *not visible*.
contentView.layer.transform = CATransform3DMakeRotation(M_PI_2, 0, 1, 0);
} completion: {
// Here you can rearrange your views (remember the view isn't visible
// right now!
//
// When the "back side" views are all ready, you can kick off the second
// half of the flip animation.
[UIView animateWithDuration:... {
// Flip *back* to the identity (not all the way around to M_PI)
// because if you flip all the way around your views will actually
// be flipped. You don't want that. Users will instinctively see
// the movement as *continuous* and interpret the flip back and forth
// animation as one continuous rotation all the way around.
contentView.layer.transform = CATransform3DIdentity;
}
}
I can't see why you couldn't use [UIView animateWithDuration...].
You could have two UIViews in each cell who's scale you animate. The first would be visible and the second would be hidden
At the half-way point of the animation, simply hide the first UIView and unhide the second and continue the animation from there.

Resources