Dissolve transition reverts UI element frame changes - ios

I'm using a modal transition to shift from one XIB to another, and I've got it all working except for one thing: at the moment that the transition begins, all the movement animations I've done on the previous view are reverted.
Here's the method I'm working with:
- (IBAction)chooseInsight:(id)sender {
[CATransaction setCompletionBlock:^{
ContainerViewController *insight = [[ContainerViewController alloc] init];
insight.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:insight animated:YES completion:nil];
}];
[self animateExit];
}
The animateExit method animates a 1-second frame movement for several UI objects, giving the effect that everything flies off the screen leaving a solid-colored background. My hope was that this solid color background would then dissolve into the next view, the ContainerViewController.
But what happens is that the UI objects fly off the screen, we see the solid color background, and then suddenly all the buttons and labels snap back so they can dissolve into the ContainerViewController.
Why is that happening? Has an image of the previous view been cached, to aid the animation? If so, can I refresh the cache before the transition? Or if not, what can I do to make this dissolve work smoothly?
Edit: In case it's relevant, I got the CATransaction bit from this answer about how to delay until the end of an animation. There's a voice in the back of my mind saying that maybe the two animations are the source of the problem, but I'm not familiar enough with iOS animations to figure out how...

all the movement animations I've done on the previous view are reverted.
Because you performed those animations by changing the frames (or centers) of those subviews. But you are also using Autolayout. You can't do that. Frames and Autolayout are enemies to one another.
When the transition comes along, layout happens. That means that the constraints are obeyed - that is what Autolayout means. But you did not change the constraints (which is what you should have done); you changed the frames. The constraints win, so everything goes back to where it was, because that is what the constraints say to do.

Related

AVPlayerLayer Popgesture Glitch

EDIT: https://bitbucket.org/KevinvandenhoekBeast/avplayerlayerpopgesture/overview <-- A simple sample project that recreates the problem, click on the video to push the detailviewcontroller, then popgesture back. The animations happen in the NavigationAnimator, and the popgesture logic is in the NavigationInteractor. The NavigationTransitioningView protocol defines the methods that are used by the animator to transition the video from one frame to the other.
Hello fellow developers,
For the past few evenings I’ve been struggling with a persistent and 100% reproducable animation glitch. I’ve been working on an app that has custom navigation transition animations that work both ways (push and pop). The transition I made is having a UIView type object moving in the transitioncontext from 1 view to the other, and so far it’s been working wonderfully using a protocol that I made for the two involved viewcontrollers, which lets them tell the animator what view they want to transition, and their respective from and to frames.
I use this on images, and on videoplayers, IE when the user clicks on an image in a collectionView the image frame animates from its origin in the original viewcontroller towards the position it should take in the detailViewController. The same happens for playing videoplayers. It works perfectly when I simply push a viewcontroller, and pop, for both videoplayers and images. However, recently I implemented a custom pop gesture (with a UIPercentDrivenInteractiveTransition subclassing class). The popgesture seemed to work wonderfully and I was looking at some beautifully lerped animation states, until I tried it with a videoplayer and the horror started.
When doing the popgesture with a videoplayer that is animated from one frame to another (as in CGRect) there is some extremely glitchy behaviour. The popgesture pretty much consistently cancels after a (very) short amount of time, generally at about 0.1 ish progress (an interesting detail: if I swipe very quick the gesture is completed and nothing bugs/glitches), but despite cancelling, the pop does (seem to) happen and I land back at the previous viewcontroller. This is where things get really funky. The visibility / frame of the viewcontroller is changed somewhat, like there is some clipping (somewhere past halfway the view cuts off), and the entire view seems to be shifted (upwards in my case), and generally repeating the popgesture worsens things. However, tapping on the view still responds as if the view is in its normal position (without the shift upwards). This leads me to believe this is some sort of a layer glitch with some of the stuff happening when animating an AVPlayerLayer with a UIPercentDrivenInteractiveTransition.
When I don't update the playerlayer containing view it’s frame while animating, the bug doesn’t occur. Similarly, when I remove the player’s layerClass override, and simply add an AVPlayerLayer as a sublayer (and update it’s frame in the layoutSubviews call), I also don’t experience the bug, but I get the side effect that the playerlayer immediately jumps to it’s final frame, despite the containing view animating properly).
I’m sincerely hoping there is someone that has a deeper knowledge in CALayers, and maybe recognizes these symptoms and could hopefully point me to what’s really causing them. Many thanks in advance.
I useful, I’m willing to upload a video of the issue.
tl;dr using a custom UIPercentDrivenInteractiveTransition subclass to lerp the frame property of a view with a layerClass of AVPlayerLayer from 1 CGRect to another causes very strange things to happen in the destination viewcontroller (its root view visually shifting / changing frame / clipping, while the touch area is as if the view isnt shifted (IE tapping a collectionViewCell highlights / selects the cell above)), despite the exact same animation logic working flawlessly when used without the UIPercentDrivenInteractiveTransition subclass popgesture handling.

Why doesn't buttonName.removeFromSuperview always remove the button?

In my swift game, we have a main game scene and then once you die in the game, it puts you on an endgame screen where your score is displayed. From this screen, the user then has the option to tap a button to change the scene back to the game. Most of the time, the button is removed and everything works normally but every so often it doesn't remove it and overlays itself over the game scene. This is the code ran by the button-
scoreLabel.removeFromSuperview()
highLabel.removeFromSuperview()
RestartBtn.removeFromSuperview()
self.RestartBtn.removeFromSuperview()
self.highLabel.removeFromSuperview()
self.menuButton.removeFromSuperview()
self.scoreLabel.removeFromSuperview()
self.scene!.view?.presentScene(GameplayScene(), transition: SKTransition.crossFadeWithDuration(0.8))
self.menuButton and all the others are probably weak references.
i.e., they become stale once the view has been removed from superview. It sometimes work, sometimes doesn't to the whim of how it is referenced in self.menuButton = <something>, which, if done in loadView or viewDidLoad, is just plain dangerous.
Different approach
I would recommend not juggling with the UI so much, and adopt a more constant referencing technique, such as hidden, or alpha = 0.
self.menuButton.removeFromSuperview()
// self.menuButton is stale. You can't re-add your button from that reference
Animate show/hide views
This will not destroy your references, and look oh so much better than removeFromSuperview:
// Hide with animation
[UIView animateWithDuration:.4f animations:^{
[self.menuButton setAlpha:0]; // Hide with animation
} completion:^(BOOL finished) {
// If you decide to remove the view permanently, do it here
// Without knowing more, I wouldn't recommend it
}];
You can then show the UI again, with or without animation:
[self.menuButton setAlpha:1]; // Show without animation

UIButton state transition animation not working for setEnabled or setHighlighted

Update
The animation is working for setEnabled=NO.
The animation for setEnabled=YES is being triggered when UIScrollView is scrolling, the UIButton is inside the scrollview and the animation for setEnabled=NO is being triggered when UIScrollView is done scrolling.
So, I think the reason why animation for setEnabled=YES is not working is because the view is moving. I am not sure but this seems to be the only logical explanation from what I have found so far. I did a test with dispatch_after() and the animation worked for setEnabled too, in other words the animation is working if it is being triggered when the view is not moving.
What I need to do ?
I have two different background images for UIButton one for UIControlStateNormal and another for UIControlStateDisabled.
I want a effect where UIButton slowly transitions over from one state to another
What have I been doing ?
BOOL enableDisable = YES;
[UIView transitionWithView:((UIButton*)object)
duration:3.3
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{ [((UIButton*)object) setEnabled:enableDisable]; }
completion:nil];
The Problem
UIButton transforms to setEnabled=NO state over the duration but no matter what I put in the options setEnabled happens almost instantly.
is there something I am missing ?
Thanks in advance for your time and response.
Unfortunately, enable or disabled state for UIView aren't part of animatable properties in apple docs. The animatable properties are:
frame, center, bounds, transform, alpha, backgroundColor, contentStretch
Reference here: UIView animation
However if you want to create custom property for animation, you can have a look at this post which describes a way to achieve it. Create a custom animatable property
I can confirm that your code works as expected both for the transition
enabled: NO -> YES
and for the transition
enabled: YES -> NO
So, my guess is that something else is happening in your app that somehow interferes with the transition. Try defining a completion block like:
...
completion:^(BOOL finished) {
NSLog(#"FINISHED? %#", finished?#"YES":#"NO");
} ];
and add a trace log before the transitionWith call or inside the animation block to check how long the transition runs from start to completion.
My guess is that while the button is transitioning something else is happening that changes its state and as a secondary effect breaks the transition. I fear that without seeing more code, it will not be possible to help you further...

Take a snapshot of a hidden UIView

I'm trying to take a snapshot of a hidden view but am running into several issues. If I try unhiding it quickly, taking a snapshot, and then rehiding it, I sometimes get a quick flicker on the screen that is pretty jarring.
toCollectionViewCell.hidden = NO;
UIView *toPlaceHolderSnapshot = [toCollectionViewCell resizableSnapshotViewFromRect:toCollectionViewCell.bounds afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
toCollectionViewCell.hidden = YES;
I'm pretty sure the flicker is caused by the afterScreenUpdates:YES, but I can't imagine that is intended behavior.
I've also tried moving the cell/view off screen instead of hiding it, but I can't be certain when that cell might be reloaded and therefore moved back into its place prematurely.
Is there a way to take a snapshot of a hidden view or a more clever way to achieve this? I need this functionality during a custom transition animation where I am pulling a collection view cell out of the collection view and then returning it back into place on dismiss. I am taking snapshots of the before/after state and then transitioning between the two during the animation.
Thanks!
Add an extra container view to your view hierarchy. Hiding the container will have the same visual effect, but you'll be able to snapshot the content of a container with ease.
I've also tried moving the cell/view off screen instead of hiding it, but I can't be certain when that cell might be reloaded and therefore moved back into its place prematurely.
This approach is probably the simplest. As long as all of your work is done on the main thread, the cell won't move during your snapshot.
You could also try archiving and then unarchiving the view (to essentially copy it):
id copyOfView =
[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:originalView]];
UIView *viewCopy = (UIView *)copyOfView;
viewCopy.hidden = NO;
(All views in the hierarchy will need to conform to the NSCoding protocol.)
Finally, you could draw your cell to a UIImage, and then display it in a UIImageView. Sample code here.

UIView switching animation for landscape and portrait scene in storyboard?

I'm trying to design different layouts for my view controller. After googling, I've got this answer from SO. Basically, the answer instructs me to setup 2 scenes, one for landscape and one for portrait, when the device rotates, hide one and show the other one.
And my question is what if I want to add a sweet animation for the rotation process? For example, since I can setup the same view on different scenes, can I set the motion path for these views and disappear? Or, if it is easier, can I add some rotation and fade out animation?
I'm quite green with Core Animation, so a detailed explanation is very useful for me. Big big thanks in advance!
There are a few options here. The main methods you are looking for to do any of this are:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration;
and
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
If you want to move the elements around the screen while the rotation is happening you will be best off changing the frames of the elements rather than creating a separate view file. Using any motion animation would require some mapping between the two and I have never heard of anything like this before. Again, if you just change the frames though, you can preform basically any screen movements you would like (for views and subviews).
To do the fading you could just throw an animation block in the first method:
[UIView animateWithDuration:duration
animations:^{
// set alphas for old and new views here.
}];

Resources