I am trying to make multiple UILabels (using a for loop and inserting them into an array) move horizontally across the screen. I have the labels, I have the array, but I cannot figure out how to use animations to get them to move. I tried using [self.array setValue #0 forKey:#"XPOSITION"]; but I do not know how to write the x position. Is there no real way to change the x position in an array? Will I have to make each one its own individual thing and use object.frame = CGRectMake(newx,y,w,h); which seems a bit tedious? Thank you in advance!
Following Aderis' advice about setting the center, you have to use a for loop to set the center of each label.
for (UILabel *label in self.array) {
CGPoint oldCenter = label.center;
label.center = CGPoint(oldCenter.x + 20, oldCenter.y);
}
Where you would change 20 to be whatever value you actually wanted to change the x coordinate by.
If you want to have it animated, you can use this:
[UIView animateWithDuration:0.2 animations:^{
for (UILabel *label in self.array) {
CGPoint oldCenter = label.center;
label.center = CGPoint(oldCenter.x + 20, oldCenter.y);
}
}];
Where you would replace 0.2 with the length in seconds you want the animation to last.
You should try using the builtin animation capabilities of UIView. You can do the following code:
[UIView beginAnimations:nil context:nil];
for (UIView* view in myArray)
{
[view setCenter:thisViewsTargetCenterPoint];
}
[UIView commitAnimations];
In the for loop, you should do all the UI positioning changes you want, and once you call commitAnimations, UIKit will do all the position changes through animations.
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 am looking to have an animation in my iOS app which is almost identical to the animation you see when you use Siri when it is displaying possible questions you can ask it.
The effect is an upward scroll of text replaced by more than come from below but the effect is one in a way where not all the questions move as one together, they seem to be independent and follow each other.
I like this animation and would like to emulate it in my app - but I haven't the first idea how to go around this. My only involvement with animations within my apps so far are ones such as CrossDissolve, FlipFromLeft, etc.
Does anyone know of a tutorial which teaches an effect like this, or can point me in the right direction to start?
Thanks to all in advance.
Having a quick look it looks fairly straight forward.
The animation can be broken down into several stages.
First, there are 5 UILabels animated separately. Each with a very short delay on the previous label.
Each label is animated like this...
Set the frame of the label to be low down on the screen and set alpha to 0.0.
Animate the frame of the label to around 300 points higher on the screen and alpha of 1.0 over a duration of around 0.2 seconds.
Animate the frame of the label to about 30 points higher on the screen over about 2 or 3 seconds.
Animate the frame of the label another 300 points higher on the screen and alpha back to 0.0 over about 0.2 seconds again.
Change text on the label and repeat.
I think the easiest way to do this would be with the method...
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
options:(UIViewKeyframeAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion
Lemme give this a try in actual code and get back to you. Code completion helps a lot.
OK, here goes
Darn, I just installed Yosemite and don't have Xcode. OK, I'll give it a try here...
- (void)animateLabels:(NSArray *)labels
{
CGFloat firstLabelStartTop = 600;
[labels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger idx, BOOL *stop) {
CGFloat delay = 0.1 * idx;
label.frame = CGRectMake(0, firstLabelStartTop + 30 * idx, 320, 21);
label.alpha = 0.0;
[UIView animateKeyFramesWithDuration:2.4
delay:delay
options:0
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:0.1
animations:^{
label.alpha = 1.0;
label.frame = CGRectOffset(label.frame, 0, -200);
}];
[UIView addKeyframeWithRelativeStartTime:0.1
relativeDuration:0.9
animations:^{
label.frame = CGRectOffset(label.frame, 0, -30);
}];
[UIView addKeyframeWithRelativeStartTime:0.9
relativeDuration:0.1
animations:^{
label.alpha = 0.0;
label.frame = CGRectOffset(label.frame, 0, -200);
}];
}
completion:nil];
}];
}
This is a first attempt without Xcode to check my code and without being able to run it at all so it might not be perfect but it should give you an idea of where to go from here.
EDIT
From #WilliamGeorges comment it looks like they used a combination of completely separate animations using the method I detailed in my blog.
You would have to do 3 separate animations on each label but its still a similar idea to what I put in this answer.
I have two UIViews (My bad it is a UIView and a UIButton) which I am animating at the same time. I originally had a view and a containerView which would animate just fine and worked like a charm.
Now only one of my UIViews will move/animate in animateWithDuration even though through debugging the frame of the other view says that it is in a position it is not.
CGRect rect = self.toggle.frame;
CGRect tabRect = self.tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:0.3 animations:^{ // animate the following:
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
NSLog(#"%f AfterAnimation", tabButton.frame.origin.x);
The toggle view moves fine, but the tabButton view does not animate or move. The strange thing is that both the "After" and "AfterAnimation" debugging code returns the same value, which suggests the frame has indeed moved. Is there a specific reason that this will not work when toggle is a UIView when it would work as a UIContainerView?
Note that if I remove the line
self.toggle.frame = rect;
tabButton will animate correctly, but if I move toggle, tabButton will not move regardless of whether it is first in the animation block or second.
Edit: I have tried moving them into separate blocks and to change the center point rather than the frame, to no avail. It seems that if the toggle view moves, the tabButton will not move.
Edit 2: The pictorial evidence.{
In the following screenshots tabButton bg is green and toggle bg is red.
Above: Initial position (toggle is off-screen) correct position
Above: The problem in question toggle is correct tabButton is not
Above: When self.toggle.frame = rect is commented out (tabButton correct, toggle not)
}
Edit 3: It's even worse than I feared.{
I have done a few more tests and even if I take the toggle change out of the animation block to make it an instant thing, the tabButton will still not animate. This makes me think the tabButton may just fundamentally dislike the toggle view and/or myself so will not move just to spite me.
}
Edit 4:{
If I change the tabButton animation to tabButton.frame = CGRectMake(10,10,100,100) the View snaps instantly to that location and animates back to its original position in the same time as the animation duration.
}
I better add more bookkeeping/TLDR information in case things aren't clear.
toggle is an instance of ToggleDraw which is a subview of UIView which I created.
tabButton is a UIButton which is part of my IB viewController and a property of the class
Both toggle and tabButton are subviews of self.view
The animations will work individually with no modifications to the logic of the rects but will not work if they are animated at the same time
toggle animation seems to take precedence over tabButton animation regardless of the order
I had a problem with the animation of an UIView created in IB (the animation didn't start from the current position of the view, and ended in the initial position).
All worked fine after sending layoutIfNeeded() to the underlaying view before the animation block:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5) { () -> Void in
...
I think it is not a problem about a UIView Animation. Maybe your toggle posiztion is related to your tabButton. For a try, your can set toggle frame to a rect lick (10, 10, 100,100), then check the result.
I've created an example of what you describe and everything seems to work fine. This is what I used:
UIView *toggle = [[UIView alloc] initWithFrame:CGRectMake(320, 64, 100, 100)];
[toggle setBackgroundColor:[UIColor redColor]];
[self.view addSubview:toggle];
UIButton *tabButton = [[UIButton alloc] initWithFrame:CGRectMake(220, 64, 100, 100)];
[tabButton setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:tabButton];
CGRect rect = toggle.frame;
CGRect tabRect = tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:1 animations:^{ // animate the following:
toggle.frame = rect; // move to new location
tabButton.frame = tabRect;
}];
What I can suggest is to make sure that the code is being ran on mainthread:
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
});
Also take into account that the log you have after the animation code is incorrect as it won't run after the animation, but rather right next to asking for the animation.
If you want code to run after the animation you should use:
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
} completion:^(BOOL finished){
NSLog(#"Finished animating!");
}];
I have found a solution to the problem. If I initialise the UIButton (tabButton) programmatically rather than through the interface builder, both views will animate simultaneously.
This however seems very hacky to me, kind of like putting a bandaid over a missing foot and hoping it will sort itself out.
I could not work out the root cause of the problem but at least I could avoid the symptoms.
If anyone knows why the views would not animate when the button was made in the interface builder post an answer here, I am interested in knowing the reason behind this.
Thanks for your help everyone.
I am performing a task in such a way that I took one image and twenty to thirty rectangular cell button.
For those rectangular cells I named them as 1,2,3,4,5,--------,30. I arranged the rectangular cells in an 6*7 matrix. Now if I click the rectangular cell button 29, the image has to find the shortest path in that matrix to reach that clicked button.
How do I perform this?
You have a single image that you want to animate to the location of one of 30 buttons? Further, you want it to move not in a diagonal, but first horizontally, then vertically? CodaFi's code is quite close to what I would do. I'd write an IBAction method that I would attach to all the buttons. Startig with CodaFi's code as a basis, here's the action method I'd suggest:
-(IBAction)animateImageToButton: (id) sender
{
button = (UIButton *) sender;
//First animate the image to the x position of the button
CGPoint fPoint = CGPointMake(button.center.x, image.center.y);
CGPoint sPoint = button.center;
//animate x position first.
[UIView animateWithDuration: 1.0f animations: ^
{
[image setCenter:fPoint];
}
completion ^(BOOL finished)
{
//Once that animation is complete, create
//a second animation to move to the button's y position
[UIView animateWithDuration: 1.0f animations: ^
{
[image setCenter:sPoint];
}];
}];
}
That code would move a single UIImageView, called Image, onto the button that was tapped.
I would not name the buttons but give them tags. I would use the following scheme:
button.tag = xCoordinate *100 + yCoordinate;
So, for example, for the third button from the left in the top row the tag would be 301. Retrieve the coordinates like this:
xCoordinate = button.tag / 100;
yCoordinate = button.tag % 100;
Now all you have to do is animate the image to the center of the button or some other point near there by changing the x and y coordinates of the image's frame.
I disagree with the tag approach that mundi's layed out, because the images would move in diagonal lines to reach their destination. I imagine a for-loop would be appropriate, so here's my approach:
-(void)animateOnLinesWithClickedButton:(UIButton*)button {
for (UIButton *image in self.view.subviews){
CGPoint fPoint = CGPointMake(button.center.x, image.center.y);
CGPoint sPoint = button.center;
//animate x position first.
[UIView animateWithDuration:1.0f animations:^{
[image setCenter:fPoint];
}
completion^(BOOL finished){
[UIView animateWithDuration:1.0f animations:^{
[image setCenter:sPoint];
}];
}];
}
}
This will animate your buttons one by one to the correct x, then y position. Use the delay variation of this method to create a more realistic effect.
I have a problem with a UIView-subclass object that I am rotating using Core Animation in response to a UISwipeGesture.
To describe the context: I have a round dial that I have drawn in CG and added to the main view as a subview.
In response to swipe gestures I am instructing it to rotate 15 degrees in either direction dependent on whether it;s a left or right swipe.
The problem that it will only rotate each way once. Subsequent gestures are recognised (evident from other actions that are triggered) but the animation does not repeat. I can go left once then right once. But trying to go in either direction multiple times doesn't work. Here's the relevant code, let me know your thoughts...
- (IBAction)handleLeftSwipe:(UISwipeGestureRecognizer *)sender
{
if ([control1 pointInside:[sender locationInView:control1] withEvent:nil])
{
//updates the display value
testDisplay.displayValue = testDisplay.displayValue + 0.1;
[testDisplay setNeedsDisplay];
//rotates the dial
[UIView animateWithDuration:0.25 animations:^{
CGAffineTransform xform = CGAffineTransformMakeRotation(radians(+15));
control1.transform = xform;
[control1 setNeedsDisplay];
}];
}
CGAffineTransform xform = CGAffineTransformMakeRotation(radians(+15));
Do you keep a total of how far the rotation is. CGAffineTransformMakeRotation are not additive. Only the most recent is used. So you are setting it to 15 each time, not 15 more each time.
Here's a super simple example of rotating a view cumulatively. This rotates the view by 180 degrees each button press.
- (IBAction) onRotateMyView: (id) sender
{
[UIView animateWithDuration:0.3 animations:^{
myView.transform = CGAffineTransformMakeRotation(M_PI/2*rotationCounter);
} completion:^(BOOL finished){
//No nothing
}];
++rotationCounter;
}