I have one UIView that I rotate manually when orientation changes, this UIView hosts a number of buttons, I want to rotate them too based on the change in orientation.
I am doing this :
CGAffineTransform rotation = CGAffineTransformMakeRotation(M_PI / -4);
[UIView animateWithDuration:0.25 animations:^
{
self.parentView.firstButton.transform = CGAffineTransformConcat(self.parentView.firstButton.transform, rotation);
self.parentView.secondButton.transform = CGAffineTransformConcat(self.parentView.secondButton.transform, rotation);
self.parentView.thirdButton.transform = CGAffineTransformConcat(self.parentView.thirdButton.transform, rotation);
self.parentView.transform = CGAffineTransformConcat(self.parentView.transform, rotation);
}
completion:^(BOOL finished)
{
}];
parentView does rotate properly, but the other buttons do not !!
do I need to perform the rotation in another call? as in I cannot rotate a button and its parentview in the same block?
Thanks
It should be enough to rotate the parentView. Make sure that you assign self.parentView.transform first. Try directly assigning a proper transform with CGAffineTransformMakeRotation() and do not rely on a current state of the transform property (like using CGAffineTransformConcat()). Don't forget to make sure the subviews are really subviews of the parentView - a [self.parentView addSubview:...]should occur somewhere. If you use IB to set up subviews this can be tricky; check the view hierarchy tree of IB!
Related
I have a gesture which action moves a UIView to point B from point A. How can I add a nice transition of the UIView moving from point A to point B. I am looking at making it slowly move from point A to point B. How would I do this? At the moment to move the item I set the frame to point B.
check this code and set frame what you want
[UIView animateWithDuration:0.75 animations:^{
button.frame = CGRectMake(10, 70, 100, 100);
}];
If you're using auto-layout (and you almost certainly are - it's the default) then #PinkeshGjr s solution is likely to have problems. With AutoLayout you can't manipulate a view's frame directly. At some point after moving the frame the view's constraints can snatch it back.
Instead, you want to create constraints that control the property/properties that you want to animate (x and y position in this case) and control-drag from the constraints into your view controller's header to create outlets.
Then you want to change the constant value on the constraints and call layoutIfNeeded inside your animation code. Something like this:
buttonXConstraint.constant = 100;
buttonYConstraint.constant = 100;
[UIView animateWithDuration:0.75 animations:^{
[self.button layoutIfNeeded];
}];
The code above assumes that you've created 2 constraint outlets and called them buttonXConstraint and buttonYConstraint.
You can achieve that by using animateKeyframesWithDuration or animateWithDuration:animations but it will be just a linear interpolation. If you want to have more control over the animation I recommend that you take a look at MTAnimation it's really a powerful and easy to use Lib for achieving beautiful animations
Cheers
Very nice helper method to use for animating view.
- (void) animateView:(UIView *)view ToFrame:(CGRect) frame withDuration:(float) duration withDelay:(float) startDelay
{
[UIView animateWithDuration:duration delay:startDelay options:0 animations:^{
view.frame = frame;
} completion:^(BOOL finished)
{
// Completion actions
}];
}
I have the following super simple animation, I'm basically rotating a view 2 radians from its original angle/center, it rotates fine my only misunderstanding is why does the view move from its original position when the rotation occurs.
[UIView animateWithDuration:1.0 animations:^{
self.someView.transform = CGAffineTransformMakeRotation( 2 );
}];
Why does the view moves when rotated with the code above?
I'm currently trying to discern the information in the CGAffineTransform Reference.
Understanding the anchor point.
I found this threads but it doesn't show a concrete answer.
Why rotating imageView, it changes position?
Thanks a lot
You need to set the anchor point of your view to rotate around.
self.somview.layer.anchorPoint = CGPointMake(0.5, 0.5);
Then start the rotation.
From Apple documentations
#property(nonatomic) CGAffineTransform transform Changes to this
property can be animated. Use the beginAnimations:context: class
method to begin and the commitAnimations class method to end an
animation block. The default is whatever the center value is (or
anchor point if changed)
Link: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CALayer_class/Introduction/Introduction.html#//apple_ref/occ/instp/CALayer/anchorPoint
image from here http://www.raywenderlich.com/9864/how-to-create-a-rotating-wheel-control-with-uikit
- As you see the anchor point is the point with the value from 0.0 - 1.0 for X and Y
when you rotate the rotation will be around these points
NOTE: you need to import QuartzCore
I am adding another answer due to #fs_tigre request. The problem is with the auto layouts in your xib file, unfortunately is it unknown why that affects the transform.
Now here is the steps I did to solve the issue:
1- first you need to get rid off your auto layout (yes, you have to)
uncheck Use Autolayout
2- remove all constraints and autoresizing masks for your view that will be rotated, as in the screenshot
(Here I have my blue box, see on the right autoresizing, nothing is selected)
I have made some changes for your rotation's code
self.someView.layer.anchorPoint = CGPointMake(0.5, 0.5);
// one degree = pi/180. so...
// rotate by 90
CGFloat radians = (M_PI/180) * 90;
[UIView animateWithDuration:1.0 animations:^{
self.someView.transform = CGAffineTransformRotate(self.someView.transform, radians);
}];
Click rotate and see the magic :)
The "anchor" of CGAffineTransformMakeRotation is the X,Y of the view. You can try this:
CGPoint center = self.someView.center;
[UIView animateWithDuration:1.0 animations:^{
self.someView.transform = CGAffineTransformMakeRotation( 2 );
self.someView.center = center;
}];
According to the Apple documentation available here the bounds property of a UIView can be animated.
In order to programmatically rotate one of my views (in this case from portrait to landscape), I implemented the following code:
float w = controlsView.bounds.size.width;
float h = controlsView.bounds.size.height;
CGAffineTransform T = CGAffineTransformMakeRotation(M_PI/2);
UIViewContentMode oldContentMode = controlsView.contentMode;
controlsView.contentMode = UIViewContentModeRedraw;
[UIView animateWithDuration:1.0 animations:^{
controlsView.bounds = CGRectMake(0, 0, h, w);
controlsView.transform = T;
}];
controlsView.contentMode = oldContentMode;
I added the two instructions for contentMode because I have read somewhere on StackOverflow that contentMode must be set to that value in order for a UIView to redraw the content while the bounds property is modified (however, in this case it does not affect the behaviour in any way).
The problem of that code is that UIView is not resized with an animation but all at once.
Executing that code, the UIView is first resized (without animation) then rotated with an animation.
How can I animate the resizing?
-- UPDATE --
I tried to combine a scaling and rotating transformation. The UIView rotates and scales in an animation, but at the end its bounds property does not change: even if it has been rotated to a landscape orientation (which should be 568x320 on my iPhone), its width and height stay at the portrait values (320x568). Indeed, the UI controls on the controlsView are deformed (wrong aspect ratio) and appears stretched.
This worked for me using UIViewAnimationOptions.LayoutSubviews (Swift):
UIView.animateWithDuration(1.0,delay:0.0,
options:UIViewAnimationOptions.LayoutSubviews,
animations:{ () -> Void in
myView.transform = myRotationTransform
myView.bounds = myBounds
}, completion: nil)
(no explicit scaling nor changing contentMode)
The problem is that when you set a transform property, all other transforms (bounds) are overwritten. To fix it you have to create one transform that combines scaling and rotating.
Example:
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(0.5, 0.5);
[UIView animateWithDuration:1.0 animations:^
{
controlsView.transform = CGAffineTransformRotate(scaleTransform, M_PI/2);
}];
You will need to calculate the scaling X and Y values based on the size you want to have at the end of the animations. You might also want to change the anchorPoint property to set a center point used for scaling (controlsView.layer.anchorPoint).
Animating your bounds doesn't change its value. It reverts to its original value once the animation ends.
Change the value of the bounds to the final value once the animation is over. I'll solve the trouble.
During a sliding animation(down, pause, then up back to the original position) of a subview, the device is rotated, so the superview is rotated. I want to keep the subview's width the same as the superview, so I need to resize it during its sliding animation.
Here is the sliding animation code:
[UIView animateWithDuration:0.3 animations:^{
self.frame = finalFrame;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:3 options:0 animations:^{
self.frame = initFrame;
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}];
This is the method that is called when I detect rotation:
- (void)rotate:(NSNotification *)notif {
// What to do here to adjust the width but also keep the sliding animation going.
}
It seems there is no auto-resizing magic that can be used here. One must:
Record the progress of the animations.
On detection of rotations, cancel the old animations, adjust the view size, and add new animations starting from the current progress.
Here is a sample project for reference: http://d.pr/f/M4UW.
You can animate the bounds of the layer to change the width. Just make the height the same and apply an animation for the bounds.
If you want both animations to have the same duration, timing function etc. then you could add them both to an animation group and add that group to the layer you are animating.
I am trying to simulate the keyboard appear animation, only using a custom subview that will show the user three buttons. Is there any way I can accomplish this with storyboard (i.e. without having to programmatically create a subview)?
Quick Answer
Yes, although you will programmatically have to set some of the subviews properties. What you want to do is have your UIViewController call:
[UIView animateWithDuration:animations:completion:]
Detailed Example
in side of whatever method should bring up the keyboard try the following:
CGFloat windowWidth = self.mainView.frame.size.width;
CGFloat windowHeight = self.mainView.frame.size.height;
// center myCustomSubview along the x direction, and put myCustomSubview just below the screen when UIViewController initially gets onto the screen
CGPoint offScreenBelow = CGPointMake(windowWidth/2, windowHeight + (myCustomView.frame.size.y/2));
CGPoint onScreen = CGPointMake(windowWidth/2,windowHeight/2);
// change the second argument of the CGPointMake function to alter the final height of myCustomSubview
// start myCustomSubview offscreen
myCustomSubview.center = offScreenBelow;
// make sure to add myCustomSubview to the UIViewController's view's subviews
[self.view addSubview:myCustomSubview];
float duration = 1.0; // change this value to make your animation slower or faster. (units in seconds)
// animate myCustomSubview onto the screen
[UIView animateWithDuration:duration
animations:^{
myCustomSubview.center = onScreen;
}
completion:^(BOOL finished){
// add anything you want to be done as soon as the animation is finished here
}];
make sure you method is being called after 'viewDidAppear:' or inside of it.
when you want to get animate myCustomSubview back down off screen make sure to do the following in your UIViewController:
// set offscreen position same way as above
CGFloat windowWidth = self.mainView.frame.size.width;
CGFloat windowHeight = self.mainView.frame.size.height;
CGPoint offScreenBelow = CGPointMake(windowWidth/2, windowHeight + (myCustomView.frame.size.y/2));
// myCustomSubview is on screen already. time to animate it off screen
[UIView animateWithDuration:duration // remember you can change this for animation speed
animations:^{
myCustomSubview.center = offScreenBelow;
}
completion:^(BOOL finished){
[myCustomSubview removeFromSuperView];
}];
If Your Subview Is Not Showing Up
As always when dealing with subviews, make sure the frame is properly set, the subview has been added to a superview with addSubview:, the subview is not nil (and that it is properly initialized), and that neither the alpha nor opacity properties of the subview are set to 0.