minimize window animation, but for iOS? - ios

I have an overlay help text in a UIView that I would like to animate up to a button in the navigationbar, in a way similar to the minimize window animation on Mac OS X.
I can do the basic animations of fading opacity and resizing windows, but this one is difficult.
Any clues how to do this kind of animation with a UIView?

There are two effects for OSX that you can set for that (if I'm understanding this right), the Scale and Genie effects. To mimic the scale one, you need to animate the scale, opacity, and positions.
Example using implicit animation:
view.layer.transform = CATransform3DMakeScale(0.2, 0.2, 1);
view.layer.position = CGPointMake(yournavbarspot.x, yournavbarspot.y);
view.layer.opacity = 0.0f;
To do the genie effect, you'd do the same thing and also alter the shape of the view as it animates. I'm not sure if you can change the geometry of a UIView, so the closest thing I can think of would be to create a mask that you alter the geometry of using UIBezierPaths.

Related

How to rotate a view in place after transform as been applied?

I'm using a variant of this code to slighty rotate a UIButton about its center:
CGFloat jiggleAngle = (-M_PI * 2.0) * (1.0 / 64.0);
self.transform = CGAffineTransformRotate(self.transform, jiggleAngle);
This code generally works as expected and rotates the button in-place, about 12 degrees counter-clockwise. However, this only works if I do not reposition the button inside my layoutSubviews method. If I do any repositioning of the button at all from its initially laid-out location, the attempt to rotate above results in the button's disappearance. If the angle I choose for rotation is an exact multiple of 90 degrees, the rotation works somehow even after a move in layoutSubviews.
I understand that my button's transform is being altered in layoutSubviews and this results in the subsequent weirdness when I attempt to rotate it with the above code. I have currently worked around this problem by placing the button I wish to rotate inside another UIView and then moving that view around as desired, but I'd like a better solution that doesn't require redoing my screen layouts.
How can I adjust/alter my button's transform after a move, so that this rotation code continues to work as expected?
The problem you are facing is that a view's frame is "undefined" if it's transform isn't the identity transform.
What you should do is use the view's center property to move it around. That works even if you've changed it's transform.
You can also apply a rotation to the view's layer instead of the view (although be aware that the layer transform is a CATransform3D, a 4x4 transformation matrix instead of a 3x3, so you need different methods to manipulate it.)

Using a UIButton to mask a moving UIView

I want to bind two views to a button's border like a mask. The effect I want is seen in the image below
So I have a custom controller that uses a PageViewController to swipe between two UIViewControllers. I also have two views (the red one and the blue one). I can track the direction and degree of the page turning, so I can link that to the movement of the two views. All I need to do is apply a mask to the moving boxes so that all the movement stays within the button.
If anyone knows how to do this, I'd be eternally grateful for the information!
My understanding is that you have a view, then you want to transition between its two subviews with a sliding animation. This is fairly straight forward.
First set "Clip Subviews" to true (either in storyboard editor or setting view.clipToBounds=true). If you want a sphere like you show then set layer.cornerRadius = view.frame.size.width/2
The you can just set the frames of your subviews- either animating them with UIView animateWithDuration or setting the frames yourself in your animation loop.
The former in code (for sliding the first element to the left and right element in):
[UIView animateWithDuration:duration animations:^{
[subview1 setFrame:CGRectMake(-subview1.frame.size.width, 0, subview1.frame.size.width, subview1.frame.size.height)];
[subview2 setFrame:CGRectMake(0, 0, subview2.frame.size.width, subview2.frame.size.height)];
}];
The reverse animation is similar. If you're doing it in your own animation loop (which sounds like what you want) then you can do the interpolation yourself pretty easily.
If you want to be more advanced with your mask (i.e. based on an image) then look at something like this: How to mask UIViews in iOS
This solution may not be the optimized one, but do the trick you want. First i wanna make sure that i understand your requirement. I understand that you wanna go away to left the red image & bring in the right blue image in the button with the sliding effect.
If my understanding meet your requirement,Here is the trick
First decide which portion of red & blue image you needed. That must be easy as you know the direction & degree of the turning the UIPageViewController.
Then Crop that decided portions from the red & blue image using the code here.
Now draw both resulted cropped image on the coreGraphics side by side (red on the left & blue on the right) & retrieve the final resulted image.
set the image to the UIButton.
Finally to acquire the round shape you can set the radius property of layer property of the button.
For the third step, how you can you draw image on coreGraphics & retrieve apple give a detail explanation here
You have to do all these steps each time you wanna update the image of the UIButton. There may be a other good way to do this :)
You can use corner radious on the button layer
CGPoint saveCenter = roundedView.center;
CGRect newFrame = CGRectMake(roundedView.frame.origin.x, roundedView.frame.origin.y, newSize, newSize);
button.frame = newFrame;
button.layer.cornerRadius = newSize / 2.0;
button.center = saveCenter;

Exaggerate perspective on a CATransform3DPerspective

I have an animation that I'm adding to a view. In the animation, I have a cell that collapses as if it's being folded. I split the view into two separate layers and apply a rotation on each layer. This is working, however, the effect is more subtle than I'd like. I'm looking for a way to increase the gap between the ends of the centerline (top and bottom edges of each layer) extends in during the animation. Basically, the halves aren't tall enough to convincingly look like they're folding backwards. Here is what I currently have mid animation, the orange lines are approximately where I would want the lines to fall during the animation:
Here is the code handling the rotation:
- (CABasicAnimation *)foldLayerToValue:(CGFloat)toValue expanding:(BOOL)expanding
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform"];
CATransform3D rotate = CATransform3DIdentity;
rotate.m34 = 1.0 / -2000.f;
rotate = CATransform3DRotate(rotateAndScale, [self radiansFromDegrees:toValue], 1.f, 0.f, 0.f);
animation.toValue = [NSValue valueWithCATransform3D:rotateAndScale];
return animation;
}
This gets added to a CATransaction that also moves the lower section down the correct amount during the animation. I tried adding a scale factor to the animation, but that has an affect on the entire layer, scaling both the top and bottom edges. I tried increasing the height of the layers as they rotate, but that also provided unsavory results.
Any ideas? Is there any way I can manually set the position on a corner by corner basis?
I think part of the problem is that my anchor points are on the center line. During rotation, the top and bottom edges are getting closer to the POV, rather than the center line getting farther away. You can see that the corners are off the screen slightly which is limiting the amount that you can see the effect. I had previously had a version with the anchor points on the top and bottom of the layers, but I was unable to get the two layers to remain "touching" as the animation occurred. I had tried adding a translate to the Z coordinate during the animation, but it seemed to have no effect on anything.
The value you are using (-1.0/2000.0) corresponds to an object being far away from the camera/eye and usually has a low perspective. If you want something that has more perspective then you should change it to something that corresponds to a smaller distance to the camera. Usually the range of circa 500-800 is a "normal" distance, so you are probably looking for something in the 200-400 range.
That is: change the .m34 value to -1.0/200.0 and there will be more perspective.

Applying just the scale component of a CGAffineTransform to a UIView

I have a UIView to which various scale and rotation CGAffineTransforms have been applied, in response to touch screen gestures.
When a scale operation completes, I want to adjust the bounds of the view to the new size, then have view to re-draw itself at a scale of 1.0, while keeping all other components of the transform the same.
To adjust the bounds of the view, I'm using:
self.myView.bounds = CGRectApplyAffineTransform(self.myView.bounds, self.myView.transform);
To "undo" the scale transform, I'm trying this:
self.myView.transform = CGAffineTransformConcat(self.myView.transform, CGAffineTransformMakeScale(1, 1));
then calling [myView setNeedsDisplay] to get the view to redraw itself.
However this does not produce the desired results and when a rotate transform is applied, the above code seems to cause what looks like a sideways translation transform to be applied too.
What's the cleanest way to "undo" just a scale transform and have the view redraw at 1:1 with all other transforms remaining intact?
I am handling something similar, and what I do is store the separate components in another struct (scale, rotation, translation). That way you can easily recreate the transform again (be careful about the order of your operations though).
One thing though: I suggest that you don't change the bounds and instead just apple a scale operation to the transform. This will avoid some potential unneeded layouts. All of this stuff can be handled purely with the transform property. The way you are doing it now is not going to change anything since applying a scale of 1 is a no-op. If you scaled up the view by 2, you would need to scale by 0.5 to get back to the original. Or, as I said above, store all the components and recreate it from the identity matrix (matrix math is fast, don't worry about that).
Save your scale values in a variable and then, try changing CGAffineTransformMakeScale(1, 1) into
CGAffineTransformMakeScale(1/totalScaleWidth, 1/totalScaleHeight)

UIView: how to "reset" transformations. CGAffineTransformScale not working as I would expect

I think my question can be summed up as how to store and reset the transform of a view. But then perhaps explaining my situation might help.
If I apply the transforms below to a view, one after another (like if I add this code to a switch or a button). I get exactly the result I would expect: the scale switches between: a view that is .55 times the size of the original view, and the view at it's original scale. Works to scale sub-views of someView too, just as I want. Ad infinitum. Perfect.
//tranformScale 1
someView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.55, 0.55 );
//tranformScale 2
someView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0 );
The trouble is I want to use this code (or similar) to scale a sub-view of self.view when an iOS device goes into landscape, to fit the sub-view to the landscape screen (and back up when in portrait). It almost works, but for some reason instead of outputting round values values for the frame of the view being scaled (as happens with a test using a button to call the transforms), progressively strange values are produced. Eventually, after about 4 rotations the sub-view flies off screen. I suspect it has to do with self.view changing shape, but then again, when I log the frame of self.view it's shape is very predictable.
By the way I am centering the view using autoresizingMask flexible margins, not using auto-layout. perhaps I should be centering the view with another type of calculation?
Thanks for reading!
I did this and it worked perfect.
self.imageview.transform = CGAffineTransformIdentity
(1) be sure not to set or get any frame data after applying transforms, it is unsupported and will yield unpredictable results;
(2) turn off your autoresizing mask flex margins and center like this:
view.center = CGPointMake(view.superview.bounds.size.width/2,
view.superview.bounds.size.height/2);
(take care to apply centering while view is at full size. i.e. BEFORE the transform if it is the resize transform, AFTER the transform if it is the identity transform)

Resources