I am trying to make a simple application where there are 3 buttons. When user clicks any of the button, they are animated(moved) to their new positions! All this works from this code:
[UIView animateWithDuration:1.2
delay:0.1
options: UIViewAnimationCurveEaseOut
animations:^
{
//Moving the Sum (UIButton which will move through this animation)
CGRect frame = Sum.frame;
frame.origin.y = -20;
frame.origin.x = (-120);
Sum.frame = frame;
.....
....
}
completion:^(BOOL finished)
{
NSLog(#"Completed");
//adding subview after the animation is complete! newView is an example!
[self.view addSubview:newView];}];
The Problem is that once i add a subview to the main view, All buttons come back to their old position! meaning they weren't moved permanently.. how can i solve this? Plz help guys!
What happens when you add a view? A layout is performed. You could have done two mistakes
You are using autolayout.
If you are using autolayout, changing frames directly is not advised and a relayout will update the views to their original position using current constraints.
You are setting the frame position in layoutSubviews, viewWillLayout or viewDidLayout.
Check where you are setting the original position. Can the method be called multiple times?
You have save the X,Y Position of the new frame & then set this frame by coding.
Related
In my app there is an animation. when I press a button the animation (lets say a box moves from the left to the right.) is performed and the ViewController is changed by "pushViewContreller". What I want is when I use popViewController from the secondViewController, the firstViewController should be opened and the box should move from right to left(reverse action). Thanks in advance.
Before navigate , save START position and END position of box, and when you come back then move box from END position to START position.
if you want to move a box then you can use this code.
First of all make two CGRect say CGRect left and CGRect right.
then in viewWillAppear of firstViewController write this code.
CGRect left = //Your left side Frame
CGRect right = //Your right side frame
if (CGRectContainsPoint(right, YourObject.center))
{
[UIView animateWithDuration:0.3 animations:^{
[YourObject setFrame:left];
}];
}
In this code viewWillAppear will call every time you when you pop secondViewController and if your box will be on right it will make animation of taking it to left.
on button click write this code
CGRect left = //Your left side Frame
[UIView animateWithDuration:0.3 animations:^{
[YourObject setFrame:left];
}];
Hope you will be happy after using this code........
Im struggling with some iOS development and I hope someone will be able to help me with an advice. I want to implement a stack of UIViews that will look like a deck of cards. User should be able to swipe out 'cards' with touch. I thought of UIScrollViews with pagingEnabled to give an impression that cards are separate. However, UIScrollView can swipe from horizontally where something appears from left or right side. I want my deck of cards to be in the middle of the screen so the user can swipe cards from the deck without seeing the card appearing on the left or right side.
Also, I was thinking of using Touch Methods ( touchesBegan, touchesMoved, etc.) to move views. It could work... but Im worried that I wont be able to reproduce proper mechanics of the card ( friction, bouncing effect when the swipe is to short and so on..).
Can someone please point me to a proper technique or advice some tutorials? I did some research already but I couldn't find anything that would be helpful.
Here is an image of the effect that I would like to achieve.
And what I want to achieve is to swipe cards out of the deck.
Thanks a lot!
I guess you want a swipe to take the top card of the stack and “shuffle” it to the bottom of the stack, revealing the second card on the stack, like this:
The animation is much smoother in real life. I had to use a low frame rate to make the animated GIF small.
One way to do this is to create a subclass of UIView that manages the stack of card views. Let's call the manager view a BoardView. We'll give it two public methods: one for shuffling the top card to the bottom, and one for shuffling the bottom card to the top.
#interface BoardView : UIView
- (IBAction)goToNextCard;
- (IBAction)goToPriorCard;
#end
#implementation BoardView {
BOOL _isRestacking;
}
We'll use the _isRestacking variable to track whether the board view is animating the movement of a card off to the side.
A BoardView treats all of its subviews as card views. It takes care of stacking them up, with the top card centered. In your screen shot, you offset the lower cards by slightly randomized amounts. We can make it look a little sexier by rotating the lower cards randomly. This method applies a small random rotation to a view:
- (void)jostleSubview:(UIView *)subview {
subview.transform = CGAffineTransformMakeRotation((((double)arc4random() / UINT32_MAX) - .5) * .2);
}
We'll want to apply that to each subview as it's added:
- (void)didAddSubview:(UIView *)subview {
[self jostleSubview:subview];
}
The system sends layoutSubviews whenever a view's size changes, or when a view has been given new subviews. We'll take advantage of that to lay out all of the cards in a stack in the middle of the board view's bounds. But if the view is currently animating a card out of the way, we don't want to do the layout because it would kill the animation.
- (void)layoutSubviews {
if (_isRestacking)
return;
CGRect bounds = self.bounds;
CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
UIView *topView = self.subviews.lastObject;
CGFloat offset = 10.0f / self.subviews.count;
for (UIView *view in self.subviews.reverseObjectEnumerator) {
view.center = center;
if (view == topView) {
// Make the top card be square to the edges of the screen.
view.transform = CGAffineTransformIdentity;
}
center.x -= offset;
center.y -= offset;
}
}
Now we're ready to handle the shuffling. To “go to the next card”, we need to animate moving the top card off to the side, then move it to the bottom of the subview stacking order, and then animate it back to the middle. We also need to nudge the positions of all of the other cards because they've all moved closer to the top of the stack.
- (void)goToNextCard {
if (self.subviews.count < 2)
return;
First, we animate the movement of the top card off to the side. To make it look sexy, we rotate the card as we move it.
UIView *movingView = self.subviews.lastObject;
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
_isRestacking = YES;
CGPoint center = movingView.center;
center.x = -hypotf(movingView.frame.size.width / 2, movingView.frame.size.height / 2);
movingView.center = center;
movingView.transform = CGAffineTransformMakeRotation(-M_PI_4);
}
In the completion block, we move the card to the bottom of the stack.
completion:^(BOOL finished) {
_isRestacking = NO;
[self sendSubviewToBack:movingView];
And to move the now-bottom card back into the stack, and nudge all of the other cards, we'll just call layoutSubviews. But we're not supposed to call layoutSubviews directly, so instead we use the proper APIs: setNeedsLayout followed by layoutIfNeeded. We call layoutIfNeeded inside an animation block so the cards will be animated into their new positions.
[self setNeedsLayout];
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{
[self jostleSubview:movingView];
[self layoutIfNeeded];
} completion:nil];
}];
}
That's the end of goToNextCard. We can do goToPriorCard similarly:
- (void)goToPriorCard {
if (self.subviews.count < 2)
return;
UIView *movingView = [self.subviews objectAtIndex:0];
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
_isRestacking = YES;
CGPoint center = movingView.center;
center.x = -movingView.frame.size.height / 2;
movingView.center = center;
movingView.transform = CGAffineTransformMakeRotation(-M_PI_4);
} completion:^(BOOL finished) {
_isRestacking = NO;
UIView *priorTopView = self.subviews.lastObject;
[self bringSubviewToFront:movingView];
[self setNeedsLayout];
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{
[self jostleSubview:priorTopView];
[self layoutIfNeeded];
} completion:nil];
}];
}
Once you have BoardView, you just need to attach a swipe gesture recognizer that sends it the goToNextCard message, and another swipe gesture recognizer that sends it the goToPriorCard message. And you need to add some subviews to act as cards.
Another detail: to get the edges of the cards to look smooth when they're jostled, you need to set UIViewEdgeAntialiasing to YES in your Info.plist.
You can find my test project here: http://dl.dropbox.com/u/26919672/cardstack.zip
Icarousel has that exact built in scrolling using UIImageViews.
https://github.com/nicklockwood/iCarousel. There's several different effects in there, I forget the name of the one you're describing.
In the Comcast Xfinity iPad app, there is a small button called "Filter by" at the bottom of the screen.
When a user touch the button, a overlay menu will slide up (like the menu UI in Android).
Can anyone give me some hints about how to create this kind of overlay menu?
Thanks.
For this you can create a UIView in your nib and make it look like the menu you need, then in viewdidload you can set its origin to be just off the screen and when you want it to appear just use an animation to slide it up into view and then slide it back off the screen when you're done.
In viewDidLoad:
CGRect frame = [your_menu_view].frame;
frame.origin.y += frame.size.height;
[your_menu_view].frame = frame;
When you are ready to show it:
NSTimeInterval animationDuration = 0.3;//play around with the animation length here
CGRect frame = [your_menu_view].frame;
[UIView beginAnimations:#"MenuSlideIn" context:nil];
[UIView setAnimationDuration:animationDuration];
frame.origin -= frame.size.height;
[your_menu_view].frame = frame;
[UIView commitAnimations];
Then use the same to get rid of it except subtract add its height.
haven't tested it but it should work.
Alright this is a newbie question, so I apologize in advance. I have a UIView which I laid out in Interface Builder off-screen. When the user presses a button, I would like to animate this view on-screen. It works, but I can't interact with any buttons in the UIView. I have read that only the view is moved when you animate and that the actual objects retain their positions, so I have tried setting the position of the UIViews' layers, but it causes my UIView menu to display an extra 81 pixels to the left. When this happens, I can interact with the buttons, but I need it to be flush with the right of the screen. Here is my code in the IBAction on the openMenu button:
CABasicAnimation *moveView = [CABasicAnimation animationWithKeyPath:#"position"];
moveView.delegate = self;
moveView.duration=0.5;
// If the menu is displayed, close it!
CGPoint currentPosView = [[entirePage layer] position];
CGPoint destView;
[moveView setFromValue:[NSValue valueWithCGPoint:currentPosView]];
if (menuDisplayed) {
// Move right to our destination
destView.x = currentPosView.x;
destView.y = currentPosView.y + 81;
[moveView setToValue:[NSValue valueWithCGPoint:destView]];
// Otherwise, open it
} else {
// Move left to our destination
destView.x = currentPosView.x;
destView.y = currentPosView.y - 81;
[moveView setToValue:[NSValue valueWithCGPoint:destView]];
}
// Animate the view
[[entirePage layer] addAnimation:moveView forKey:#"move"];
// Set the final position of our layer
[[entirePage layer] setPosition:destView];
menuDisplayed = !menuDisplayed;
And then in the animationDidStop method:
CGPoint currentPosMenu = [[menuBar layer] position];
if (menuDisplayed) {
currentPosMenu.x -= 81;
} else {
currentPosMenu.x += 81;
}
[[menuBar layer] setPosition:currentPosMenu];
Help???
You shouldn't need to mess with the layers. You could probably achieve the effect you want by laying out the views in Interface Builder at the positions they will be in after they are animated into view. In other words, first lay things out in their final/correct positions. Don't do anything funky in IB.
In your code, after the app is launched or in a -viewDidAppear: method if you're using a UIViewController, you could offset the views by some amount and then restore them to their original position in an animation block:
myView.transform = CGAffineTransformMakeTranslation(0,81);
[UIView beginAnimations:#"slideUp" context:nil];
myView.transform = CGAffineTransformIdentity;
[UIView commitAnimations];
As for the lack of user interaction on your views, that could be a number of things. If all of your views are subviews of something like a UIImageView, UIImageView has userInteractionEnabled set to NO by default (at least if you build one in code - I'm not sure off hand what IB's default settings are for an image view). It could also mean that the superview of your views is actually too small and its frame does not contain the subviews. (If clipping was turned on for the superview, you wouldn't even be able to see subviews that are having this particular problem.)
EDIT:
I read things more carefully and see you want things to move in response to a button press, so in other words the views will need to be hidden from the start. In that case you could still lay everything out in IB in their final position, but just set them as hidden in IB. When you push the button, set the views visible (hidden=NO) before translating them. That should allow them to be laid out in their final/correct place in IB making sure that they are positioned on the proper superviews, etc while still getting the animation effect you want.
Forget about the animations for now; you should move your view by setting its frame property.
E.g.:
CGRect menuFrame = entirePage.frame;
if (menuDisplayed)
{
menuFrame.origin.x += 81;
}
else
{
menuFrame.origin.x -= 81;
}
entirePage.frame = menuFrame;
// Do your animation here, after the view has been moved
Don't worry about the animation until you've verified that your view is being placed in the correct location. Once you've verified that that code is working correctly, then do your animation after you set the frame property.
Note that you probably don't need to use the CoreAnimation API at all, unless you're doing something complex. UIKit can do animation on its own. E.g:
[UIView beginAnimations:#"MyAnimation" context:NULL];
myFirstView.frame = ...;
myOtherView.frame = ...;
[UIView commitAnimations];
In the app I'm working on the user taps on a tableview to zoom it up to full view from a "thumbnail" or a miniature view. Everything is working great except for a somewhat annoying animation glitch or whatever. The thing is I'm using the code below:
if ([subview respondsToSelector:#selector (name)] && [subview.name isEqualToString:self.labelListName.text])
{
[self.tabBarController.view addSubview:subview];
CGRect frame = CGRectMake(35, 78, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
subview.frame = frame;
CGAffineTransform scale = CGAffineTransformMakeScale(1.39, 1.39);
CGAffineTransform move = CGAffineTransformMakeTranslation(0,44);
CGAffineTransform transform = CGAffineTransformConcat(scale, move);
[UIView animateWithDuration:0.15
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
subview.transform = transform;
}
completion:^(BOOL finished){ [self goToList],subview.hidden = YES; }];
}
- (void)goToList
{
self.gotoWishList = [[WishList alloc] initWithNibName:#"WishList" bundle:nil];
self.gotoWishList.hidesBottomBarWhenPushed=YES;
self.gotoWishList.name = self.labelListName.text;
[self.navigationController pushViewController:self.gotoWishList animated:NO];
self.gotoWishList.scrollLists = self;
[WishList release];
}
And when doing the animation the transfer between the zoom view and the actual view the user is going to interact with is not completely perfect. The text inside the cell jumps a little when switching between the views. The problem lies in the translation matrix. If I skip that I can get the animation to work perfectly but then of course I need to move the miniature view down in the GUI which is not an option. If I instead do the animations in another order (move, scale) then it works better. I still get a jump at the end but it looks better, as everything jumps...and not just the text.
So...basically my question is how can I make this animation fluent. I read that the CGAffineTransformConcat still does each animation separately, and I really need both animations (scaling and moving the list) to be ONE fluent animation.
Thanks for any tips!
I think you will have to nest views/graphic-context to get what you want. The animation system doesn't support simultaneous animations because the mathematics of doing so requires an exponential amount of computational power. You might be able to trick it by sliding one view while enlarging the other.
I'm not sure about that as I have never had need to try it.
You might also be getting a jerk or skip from the tableview itself. The bounce at the top and end of scrolls can produce effects if you radically resize the table on the fly. I would turn all that off and see if you still have the problem. You might also want to test on the view independent of the tableview to make sure the problem is with the animations and not the tableview moving itself.