CATransform3D rotation with Label as subview - ios

I am having one issue about CATransform3D.
I have a view A, which contains a label B. B's layer position is the center of A. B is used to display notification messages. I want to rotate A by 180 degree, of course the text in B will be upside down. So I have to rotate B by 180 degree too. Everything seems straight forward and actually in the simulator it works fine.
But when I loaded on the device, A and B did rotate, but B's position was changed. Now I can only see half of B, the other half is out of A.
My guess is that, when rotating A, because B is related to A's coordinate system, which was flipped 180 degree, B's location is changed.
But I want to know if anybody has ideas how to fix this problem or if you guys have better ways to approach.
Thank you very much.
UPDATE
I still can't figure out why B's position gets shifted, but I came up with another method to implement the same animation. A little bit tricky.
The key is animation.autoreverse, we know that when you rotate the label's super view by Pi, the text in the label will be rotated upside down. So what I did was rotate the super view by Pi/2, keep the same duration and set autoreverse = YES, what it will do is that it will rotate the super view by Pi/2, then rotate it back to the initial state. The result turns out that the view isn't get rotated at all, but for the user's vision, it is rotated.

I've found that subLayers and subviews of views don't always respond well to doubled animations (esp. alpha and center changes). If you must (as it is a little more expensive than normal), use UIViewAnimationOptionAllowsAnimatedContent, which will force a redraw instead of a 'snapshot' that is animated.

Related

How to temporarily freeze a node in front of the camera using ARKit, SceneKit in Swift

I built a complete structure as a node (with its child nodes) and the user will walk through it using ARKit.
At some point, if the user cannot continue because of some real obstacle in the real world, I added a "pause" button which should freeze whatever the user currently sees in front of the camera, the user could then move freely to some other open space and when the user will release the pause button he/she will be able to resume where they left off (only someplace else in the real world).
A while ago I asked about it in the Apple Developer forum and an Apple Frameworks Engineer gave the following reply:
For "freezing" the scene, you could transform the anchor's position (in world coordinates) to camera coordinates, and then anchor your content to the camera. This will give you the effect that the scene is "frozen", i.e., does not move relative to the camera.
I'm currently not using an anchor because I don't necessarily need to find a flat surface. Rather, my node is placed at a certain position relative to where we start at (0,0,0).
My question is how do I exactly do what the Apple engineer told me to do?
I have the following code which I'm still stuck with. When I add the node to the camera (pointOfView, last line of the code below), it does freeze in place, but I can't get it to freeze in the same position and orientation as it was before it was frozen.
#IBAction func pauseButtonClicked(_ sender: UIButton) {
let currentPosition = sceneView.pointOfView?.position
let currentEulerAngles = sceneView.pointOfView?.eulerAngles
var internalNodeTraversal = lastNodeRootPosition - currentPosition! // for now, lastNodeRootPosition is (0,0,0)
internalNodeTraversal.y = lastNodeRootPosition.y + 20 // just so it’s positioned a little higher in front of the camera
myNode?.removeFromParentNode() // remove the node from the Real World view. Looks like this line has no effect and just adding the node as a child to the camera (pointOfView) is enough, but it feels more right to do this anyway.
myNode?.position = internalNodeTraversal // the whole node is moved respectively in the opposite direction from the root to where I’m standing to reposition the camera in my current position inside the node
// myNode?.eulerAngles = (currentEulerAngles! * -1) — this code put the whole node in weird positions so I removed it
myNode?.eulerAngles.y = currentEulerAngles!.y * -1 // opposite orientation of the node so the camera will be oriented in the same direction
myNode?.eulerAngles.x = 0.3 // just tilting it up a little bit to have a better view, more similar to the view as before it was locked to the camera
// I don’t think I need to change the eulerAngles.z
myNode!.convertPosition(internalNodeTraversal, to: sceneView.pointOfView) // I’m not sure I wrote this correctly. Also, this line doesn’t seem tp change anything
sceneView.pointOfView?.addChildNode(myNode!) // attaching the node to the camera so it will remain stuck while the user moves around until the button is released
}
So I first calculate where in the node I'm currently standing and then I change the position of the node in the opposite direction so that the camera will now be in that position. That seems to be correct.
Now I need to change the orientation of the node so that it will point in the right direction and here things get funky. I've been trying so many things for days now.
I use the eulerAngles for the orientation. If I set the whole vector multiplied by -1, it would show weird orientations. I ended up only using the eulerAngles.y which is the left/right orientation and I hardcoded the x orientation (up/down).
Ultimately what I have in the code above is the closest that I was able to get. If I'm pointing straight, the freeze will be correct. If I turn just a little bit, the freeze will be pretty close as well. Almost the same as what the user saw before the freeze. But the more I turn, the more the frozen image is off and more slanted. At some point (say I turn 50 or 60 degrees to the side) the whole node is off the camera and cannot be seen.
Somehow I have a feeling that there must be an easier and more correct way to achieve the above.
The Apple engineer wrote to "transform the anchor's position (in world coordinates) to camera coordinates". For that reason I added the "convertPosition" function in my code, but a) I'm not sure I used it correctly and b) it doesn't seem to change anything in my code if I have that line or not.
What am I doing wrong?
Any help would be very much appreciated.
Thanks!
I found the solution!
Actually, the problem I had was not even described as I didn't think it was relevant. I built the AR nodes 2 meters in front of the origin (-2 for the z-coordinate) while the center of my node was still at the origin. So when I changed the rotation or eulerAngles, it rotated around the origin so my nodes moved in a large curve and in fact also changed their position as a result.
The solution was to use a simdPivot. Instead of changing the position and rotation of the node itself, I created a translation matrix and a rotation matrix which was at the point of the camera (where the user is standing) and I then multiplied both matrices. Now when I added the node as a child of the camera (pointOfView) this would freeze the image and in effect show exactly what the user was seeing before it was frozen as the position is the same and the rotation is exactly around the user's standing position.

Rotate coordinate System of an UIView

Im a complete beginner, so please excuse my maybe stupid question.
Im using an UIView "superview" with many children-images to perform a rotation of the images around an arbitrary point:
superview.transform = CGAffineTransformMakeRotation(M_PI/2);
This works perfect, but it seems like this is rotating the coordinate system of the superview as well. This in turn messes up all following steps. I find hundreds of posts and infos concerning rotating images and for mac development there obviously is a method for rotating coordinate systems. I just don't find anything for iOS.
Is there a way to rotate a view without rotating the coord system / how can I rotate the coord system without influencing the images?
Thanks for any help.
it seems like this is rotating the coordinate system of the superview as well
You're right -- rotating the coordinate system of the superview is exactly what you're doing:
superview.transform = CGAffineTransformMakeRotation(M_PI/2);
Is there a way to rotate a view without rotating the coord system
Sure, just set the transform property for your view rather than for its superview. For any view someView, you can say:
someView.transform = CGAffineTransformMakeRotation(M_PI/2);
Note that this actually rotates the view's own coordinate system -- it doesn't rotate the view itself. That means that all your drawing will be rotated, but the view's frame will remain the same. You might need to adjust the frame to account for this. For example, if you have a rectangular view that's normally 100px high and 300px wide, you might want to make it 300px high and 100px wide to ensure that the content is all still visible.
If you're creating a custom view, you can of course refer to instances of that view from inside its own code using self:
self.transform = CGAffineTransformMakeRotation(M_PI/2);

UIView perpetual linear animation with collision detection

I'm trying to make a circular UIView animate in a particular direction until it collides with the borders of the view (which is full screen, so basically the borders of the device), at which point it will reflect off it and continue on its way infinitely. However, I'm not really sure how to pose my question, so I'm having trouble finding any information on it. I already have the view, the direction it will move in, and its velocity. I'm just not sure how to handle an animation like that.
Any advice is much appreciated! Thanks!
Use a CADisplayLink to continuously update the position of the view, synced with the refresh rate of the screen. The update method that that display link triggers will take into account the current x and y velocity of the view and update the frame based on it. If at ever point the x- or y-coordinate goes under or over a limit (0 or screen width/height), reverse the appropriate velocity value and recalculate the position.

layer rotation: CGAffineTranslate, CGAffineRotate and AVMutableVideoCompositionLayerInstruction

for days now I am trying to solve this, apparently I just can't get affine transforms.
Basically, I would like to rotate a movie multiple times by 90 degrees. There is a good example here:
Rotating Video w/ AVMutableVideoCompositionLayerInstruction
but this works only for the first rotation, while I would like to rotate it from portrait to landscape, back to portrait, back to landscape.
Whatever I tried, I get absolutely weird results. Unfortunately, even after much reading, my understanding of affine transforms obviously did not improve either.
So I am basically asking for a layout of an algorithm which will
rotate the layer of an AVMutableVideoComposition by 90 degrees,
regardless of its current orientation.
Apply a translation to move the layer into the center.
This should be called each time I click on a button.
But whatever I try, the layer is rotating correctly, but the translation is moving the layer out of the center. What is the right way to do this?
But whatever I try, the layer is rotating correctly, but the translation is moving the layer out of the center. What is the right way to do this?
The right way to do this is with 2 transforms: one to rotate, and one to translate. At the beginning of the processing, make sure that your current position is set - by assigning the current transform at the start of processing. Then, create two independent transforms - one for rotate, and the other for translation. You can 'concat' these two transforms to create a new one which does both operations - but if you expect the translation to function 'in place', be sure to re-set the original transform before you apply the new one.

How can I reproduce a "box" transition animation in iOS?

I want to build an animated transition between two view controllers in iOS, resembling the "Box" transition in PowerPoint or the "Reflection" transition in Keynote.
You can see it here, at 2:10:
http://youtu.be/1fLQg5hFQQg?t=2m10s
What's the best way to do this?
Thanks!
That would be a complex animation to recreate. You'd need to use a CAAnimationGroup that grouped several different animations running at once. You'd want to animate a rotation around the y axis with the center of rotation lifted off the screen, on both the view controller that is animating away and the view that your are animating into place.
You would have to tweak the transform to make it draw with perspective (you add a small value to the .m34 record in the transform). That's because CA animations are orthographic by default (they don't show perspective.)
The reflections could be created using a special subclass of CALayer that lets you create duplicates of a layer. I'm blanking on the name of that layer subclass at the moment. You'd set up 1 duplicate with a scale of -1 on the y axis to flip it upside down, and a darkening effect. I've never done it myself, but I've seen several examples in books and online.

Resources