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.
Related
How do I produce an animation that simulates the burning effect of fire consuming an UIView from top to bottom in Swift?
I found Fireworks, an app that allows users to tweak and try out different settings of CAEmitterLayer with instant results. The effects are great for applying to a whole screen but how would I use it for my purpose - where the UIView must disappear as the fire consumes it from one end to the other?
Is there some tutorial on consuming UIViews with fire using the particle emitter anywhere? I know that I’m supposed to show some code but anything I put here would be irrelevant. I’ve also exhausted my search engine looking for something similar. That’s how I found the Fireworks app actually.
This seems to be a use case that shouldn't be uncommon.
I haven't done much with CAEmitterLayer, so I decided to try my hand at this.
I created a project that does this an posted it on Github. It uses the approach in this Youtube video as a starting point. You can download it here:
FireEmitter project
Here is a small thumbnail of what it looks like:
The project includes a custom subclass of UIView called BurnItDownView
The BurnItDownView is meant to contain other views.
It has one public method, burnItDown(). That triggers the animation.
There are multiple parts to the animation:
A CAEmitterLayer set up to simulate flames burning off a flat surface:
An animation that lowers the emitter layer from the top of the view to the bottom,
A CAGradientView applied as a mask to the view that starts ot fully opaque (with colors of [.clear, .white, .white] and locations of [-0.5, 0, 1] (where the clear color is above the top of the view) and animates the locations property of the gradient view to mask away the view contents from top to bottom. (Animating the locations property to [0, 0, 0], so the entire gradient layer is filled with clear color, fully masking the view's layer.)
Once the view is fully masked, it starts lowering the "birthRate" of the emitter layer in steps until the birth rate is 0. It then holds this step for 2 seconds until all the flame particles have animated away.
Once the flame is fully "extinguised", it resets the locations array to the original value of [-0.5, 0, 1]. This causes an "implicit animation" so the view animates back from the bottom, but quickly
Finally, it resets the emitter layer and emitter cells back to newly a newly created emitter layer and emitter cell to get it ready for the next pass of the animation. (I couldn't figure out how to restore the emitter back to its original state. It was simpler to just create new ones.) It also invokes an Optional completion handler passed to the burnItDown() method. (The app's view controller uses the closure to re-enable the "Burn it down" button.
I was once in your shoe before and came across this Open source library called particle animations.
I would NOT recommend using the library itself since it's deprecated. But I would recommend referring to its source code to get an idea of how to use CAEmitterLayer and CAEmitterCell to make the looks of a Fire!
As you could see from its readme, it has direct examples of Fire. It also states that even Apple and Facebook uses CAEmitterLayer and CAEmitterCell to produce the effect of a fire.
Feel free to ask for more questions.
When swiping between stories in Instagrams new feature "Stories" (you know that cube-like transition when going from one story to another) I can't manage to understand how they do it!
First of all, if you dig deeper into the functionality you find that it works exactly like the UIPageViewControllers transition:
- It bounces when swiping fast from one view to another.
- You can pause the swipe in the middle of the transition by touching the screen.
The developing team couldn't have used a solution based on the more known workarounds out there, e.g:
https://www.appcoda.com/custom-view-controller-transitions-tutorial/
Because as far as I know my two statement above is not possible to achieve with anything else than the PageViewController.
This leaves me thinking that the Instagram Developer Team gained access to a new transition style for the PageViewController, also known as Cube-scroll, or is it a workaround that I'm not aware of?
Any ideas?
I took a shot at recreating this functionality a while back. You can check the source code on GitHub: https://github.com/oyvind-hauge/OHCubeView
I'm using a scroll view (with paging enabled) and, for each subview I'm manipulating these as a function of the given view's current x-offset in the scroll view. The actual animations are done on each subview's layer using Core Animation (more specifically, transforming an identity matrix, given by CATransform3DIdentity, using the method CATransform3DRotate).
The shadow effects are also applied to the subview's layers (view.layer.opacity), with the amount of shadow determined by how much of the view is showing on screen.
My implementation solves both of your concerns (bounces when swiping, can pause swipes). I'm sure this could have also been implemented using the a UIPageViewController, but I hate working with those.
I think you are overthinking the controller's part here. The effect can easily be achieved using a CATransformLayer and three-sided cube-like view structure, where there is one view which aligns with the screen plane, and two others rotated -90 and 90 degrees on their y axis. Then, getting a pan gesture to rotate the scene. After a successful 90 degree turn (in either direction), you can either quickly reset the scene (so that keeping on rotating appears as if continues, but actually the camera shifted back to initial position) or you can have a full 360 degree rotation, and just update previous and next "pages". A single controller can handle this scene. If you prefer to have each page as a controller, it is possible, you can still use one controller for the scene, and then use the page controllers as child controllers, and setting their views as described above.
See this article for more information on CATransformLayer. Their example already creates something that is quite close to what you need.
In storyboard, I have buttons attached to main view as siblings to SCNView(both buttons and SCNView are subviews of main view). At launch time, the buttons appear on screen before the SCNView does. This is quite unfortunate. Do you have any idea how to fix this?
//edit: added button programatically in viedDidLoad: as a subview of SCNView..Still the same problem.
SettingsButton and View appear before SceneView. This happens on iPhone 5,5c and lower and on iPads.(3 for sure, don't know for others)
Code for adding view programatically:
let uv = UIView(frame:myFrame)
uv.backgroundColor = UIColor.blackColor
sceneView.addSubview(uv)// scene view from storyboard
Thats it. How can iOS take 5-10s to load an app, but than rushes with adding views to the screen in an unpredictable order.. Couldn't it just take 0.2s more and present view properly? Really stupid problem
I imagine if you check via debugger breakpoints or logging in viewDidLoad, you'll find that the SCNView instance is indeed in the view hierarchy immediately.
What you're seeing is that said view's SceneKit content does not appear immediately. Depending on what you're doing with SceneKit, it may take some time to load content and prepare for rendering, and during that time the rest of the view hierarchy remains visible. That delay is probably unavoidable — you might be able to minimize it by optimizing asset formats, reducing the amount of stuff in your scene, etc. So it sounds like the key issue here is getting your app launch experience to look the way you want while accounting for the delay.
A good way to do that might be to show a splash image of some sort until SceneKit is ready to display. You could put a UIImageView in your storyboard, set up to obscure everything with a splash screen (possibly the same launch image iOS displays before your app takes over), then hide that image view (with a fade out even?) when SceneKit starts rendering.
To find out when SceneKit starts rendering, make one of your objects the scene renderer delegate and implement renderer:didRenderScene:atTime:. That method gets called on every frame, so you can use it to catch when the first frame has been rendered (and then ignore it for subsequent frames).
Lately Ive been working with moving my frame to make up for the 20point gap when the status bar is not showing in landscape mode. I have finally gotten what I want, yet there is one problem that kind of annoys me. Let me explain: First I was using a call in viewDidLoad to move the frame, but that doesn't do anything to the frame, because I suppose the frame gets drawn after viewDidLoad. So I tried placing my call in viewDidAppear. That works smoothly, but sadly the user "sees" the repositioning; you can watch the frame being replaced with the bare eye. So I was wondering if theres anything I can do to prevent this, any workarounds. (just for good explanation this call is to take care of view frame when the user updates/go to another view controller when already in landscape. I have other functions to take care of rotation; which obv works)
Any help appreciated. I tried to be as specific as possible.
Thanks!
What is the best way to implement a smooth reversing animation which tracks touches? I am referring to those animations in which, for example, if the user executes a swipe gesture some elements smoothly animate on screen, and others off, but if the user instead slowly drags a pan gesture back and forth the same objects will move forward/backward as a percent in accordance with the touch position. This is seen in many app intros and also in transitions. I have found
One tutorial which discusses the built-in facility for this but it is only between view controller transitions, not providing the full granular control I see in many apps (http://www.doubleencore.com/2013/09/ios-7-custom-transitions/)
Jazzhands, which is a kit by IFTTT, but this is a packaged solution that might not cover how the solution is best implemented at a lower level (https://github.com/IFTTT/JazzHands)
A question here for which one answer shows how you might execute an animation after a gesture ends (iOS Touch, Gestures, Animation)
What I don't grasp - and I'm comfortable using CAAnimations and gestures - is how something can be both animated and interactive.
Typically, when I create an animation, I commit the animation and it goes from start to finish. While I could interrupt the animation as touches continue, that seems like it would be stilted.
On the other hand, moving things in response to user input is easy, but that is not animated.
How is the effect achieved where something can change according to an animation, but also have that exact same animation occur tied to touches, and yet still also have it so that although the animation reaches completion it doesn't really "finish" (become irreversible) unless the user releases touch, while at any point during interaction if the user releases panning then the animation either reverts backwards to its starting position or animates to completion depending on the last touch location and velocity. These requirements are baffling.
The glimpses of this technique I see all involve keyframe animations, but what I don't understand is where the touch events intersect with an animation to create these smooth effects I see.
Any tips, examples, or tutorials are most welcome.
What I don't grasp - and I'm comfortable using CAAnimations and gestures - is how something can be both animated and interactive.
It is because, having set up an animation, you can set that animation to any "frame" you wish. Thus you can track the animation in correspondence to the movement of a gesture.
The way this works is that an animation is a feature of the render tree, belonging to a CALayer. CALayer implements the CAMediaTiming protocol. The timeOffset of a CALayer thus determines what "frame" of an animation that layer displays. If a complex animation involves many different layers, no problem; just set the timeOffset of their mutual superlayer, to control the frame of the entire animation.
That in fact is exactly how the new iOS 7 interactive custom transition feature works (to which you rightly refer in your question). For instance, in this example code:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/iOS7bookExamples/bk2ch06p296customAnimation2/ch19p620customAnimation1/AppDelegate.m
... I keep updating the UIPercentDrivenInteractiveTransition to tell it how far through the gesture the user is, and the animation therefore tracks the gesture. Now ask yourself: how the heck is this possible???
Well, the UIPercentDrivenInteractiveTransition, in turn, behind the scenes, keeps adjusting the layer's timeOffset to portray the animation at the corresponding frame. (You can actually add logging code to my example to see that this is true.)
Moreover, when I end the gesture at an incomplete point, the animation either hurries to its end or runs backwards to its beginning - again, this is because of the CAMediaTiming protocol, which lets you change the speed of the animation, including a negative value to run it backwards.