Repeating UIButton animation and change background - objective-c

Can someone please tell me why this doesn't work?:
//Changing the Images
- (void) squareOneColour {
NSUInteger r = arc4random_uniform(5);
[self.squareOne setBackgroundImage:[self.colorArray objectAtIndex:r] forState:UIControlStateNormal];
}
//Moving buttons
- (void) squareOneMover {
NSUInteger r = arc4random_uniform(3);
[self.squareOne setHidden:NO];
CGPoint originalPosition = self.squareOne.center;
originalPosition.y = -55;
[self.squareOne setCenter:originalPosition];
CGPoint position = self.squareOne.center;
position.y += 760;
[UIView animateWithDuration:r + 3
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
[self.squareOne setCenter:position];
}
completion:^(BOOL complete) {
if (complete) {
[self squareOneColour];
[self squareOneMover];
}
}
];
}
I am trying to get a UIButton to animate down the screen and when the animation is finished, for it to repeat but with a different background. The code above seems it should work but the second animation is never completed. (The UIButton is animated once, then everything stops). I have tried putting the code shown below (into the completion block), with an outside NSUInteger method creating a random number for r but it never works. But it does work when I replace r with a different number, like 1 or 2.

I found the answer. After playing around with it, I found that the function I wanted the UIButton to do was only fit for a UIImageView. I guess I'll have to make an invisible button over the UIImageView

You're stepping on your own feet. Step out to the next cycle of the runloop and all will be well:
[self squareOneColour];
dispatch_async(dispatch_get_main_queue(), ^{
[self squareOneMover];
});

Related

objective-c animations - how to set end position, direction and type

I've seen many examples and read tutorial on animations,
all examples look +- the same like so:
- (void)showAnimation
{
[UIView animateWithDuration:0.3f animations:^{
backgroundView.alpha = 1.0f;
}];
[UIView animateWithDuration:1.0f
delay:0.0f
usingSpringWithDamping:0.4f
initialSpringVelocity:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^{
CATransform3D init = CATransform3DIdentity;
alertView.layer.transform = init;
}
completion:^(BOOL finished) {
if( [self.delegate respondsToSelector:#selector(alertViewDidAppear:)] && finished) {
[self.delegate alertViewDidAppear:self];
}
}];
}
what I'm failing to understand is in the animation block where:
1. I set the beginning position
2. set the end position
3. the direction of movement
4. type of animation (fly-in/ turn/ fade-in/appear etc.)
There is no "starting position". There is no "direction of movement" or "type of animation". Animation is a change over time. The view is a certain way at the time your code runs. UIView animation has 6 possible view properties. You change any of those in the animation block (which states the time) and the change is animated - that is, instead of the change just happening, kaboom, it is performed over the given time.
That's all there is to it (as far as UIView class-method animation is concerned).

create animated circle and rectangle in uiview

i'd have to create some animated shapes in a uiview in my iphone app (with xcode), like these ones (circle , rectangle)
The first one should be a circle increasing and decreasing its radius, from 0 to x and viceversa; the second one is a rectangle increasing its length from 0 to x, and viceversa. The shapes should be animated infinitely... until the user taps some button to stop the animations at some point. How can i achieve those particular draws and animations? What's the best way to achieve that? And how to stop them?
Many thanks for any advice.
In my case I had to animate the movement of subviews of my UIView object and I accomplished it like this: break up the continuous animation into logical parts, animate a part and configure this animation to start another animation after it is done animating. Below you see my code as is (what it does is move subviews that are placed on a circle along that circle over a given angle).
- (void) animatePart: (NSString*) animationId finished: (NSNumber*) finished context: (void *) context
{
// the context contains an NSNumber which is the angle remaining to turn the pieces
CGFloat displacement = [trackSet currentDisplacementOnTrack:drag.trackIndex];
NSMutableDictionary* contextDict = (NSMutableDictionary*) context;
Move * move = (Move *) [contextDict objectForKey:ANIMCTX_MOVE];
//NSNumber *direction = (NSNumber *) [contextDict objectForKey:ANIMCTX_DIRECTION];
printf("> EVC animatePart: displ: %3.3f ", displacement);
if (FABS(displacement) < 0.1) {
printf(", ending animation\n");
// this is the last piece of animation
[trackSet completeMoveOnTrack:move.trackRef];
[self updatePiecesAnimatedFromModel];
self.animationContext = nil;
}
else {
CGFloat dir = displacement == 0 ? 0 : displacement / FABS(displacement);
CGFloat delta = -ANIM_DELTASTATION * dir;
printf(" EVC animatePart: delta: %3.3f\n", delta);
CGFloat newDelta = FABS(delta) < FABS(displacement) ? delta : -displacement;
[trackSet registerMoveOnTrack:drag.trackIndex by:newDelta];
[UIView beginAnimations:animationId context:context];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animatePart:finished:context:)];
[self updatePiecesFromTrack:drag.trackIndex];
[UIView commitAnimations];
}
printf("< EVC animatePart\n");
}
As you can see, the UIView methods setDelegate: and animationDidStopSelector: are used to configure this animation to call a certain method after it's done animating. The selector passed here is that of this same method. This sounds like a recursive method (it even has a stop condition, just like recursive methods normally do), but it's not. So no worries about stack overflows ;-)
For completeness, to kickstart this animation I use the method below (copied verbatim from my code).
- (void) animateMove: (Move *) move direction: (int) direction
{
printf("> EVC animateMove: %d\n", direction * move.stations);
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithObject:move forKey:ANIMCTX_MOVE];
[dict setObject:[NSNumber numberWithInt: direction] forKey:ANIMCTX_DIRECTION];
self.animationContext = dict;
//[trackSet openMoveOnTrack:move.trackRef];
// the move has already been carried out in the model, but is not yet reflected in the UI
// we don't need to call trackSet's openMoveOnTrack and closeMoveOnTrack
// for the animation we initially set the track's current displacement to that of the move
// then in a series of animated steps we decrement that displacement gradually to zero,
// while showing the intermediate results in the UI.
CGFloat moveDisplacement = (CGFloat) (move.stations);
[trackSet registerMoveOnTrack:move.trackRef by:moveDisplacement];
[self animatePart:#"displacementAnimation" finished:[NSNumber numberWithInt:0] context:dict];
printf("< EVC animateMove: \n");
}

Objective c - adding threading to UIView animation method results in only one animation running

I've written a method, sortAndSlideHandCards(), that moves 6 UIButtons. Each UIButton is moved to the same position. This is done via a for-each loop and the animateWithDuration method being called on each UIButton.
This method is called for a number of players at the same time. Currently the behaviour results in UIButtons from each player moving but only one at a time. No more than one UIButton can move at any time, as if each animation is waiting for whatever animation that is currently running to stop before attempting it's own animation, essentially the code is executed sequentially for each player/UIButton. I hoped threading would help me fix this.
when I added the threading code:
backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
sortAndSlideHandCardsGroup = dispatch_group_create();
for(Player* player in _playersArray) {
dispatch_group_async(sortAndSlideHandCardsGroup, backgroundQueue, ^(void) {
[player sortAndSlideHandCards];
});
dispatch_group_wait(sortAndSlideHandCardsGroup,DISPATCH_TIME_FOREVER);
I found that only the first UIButton animation is triggered for each player and that the code gets held up in the runloop "while" because "_animationEnd" never gets set as it would appear the second animation never gets going.
I can see the method launching in its own thread
- (void) sortAndSlideHandCards {
NSLog(#"PLAYER:sortAndSlideHandCards");
CGPoint newCenter;
Card* tempCard = nil;
int count = 1;
float duration = 0.2 / _speedMultiplyer;
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for(Card *card in _handCards) { //move cards in hand to one postion in hand
if(count == 1) {
tempCard = [[Card alloc] init:_screenWidth:_screenHeight :[card getNumber] :[card getCardWeight] :[card getSuit] :[card getIsSpecial]];
[tempCard setImageSrc: _playerNumber :!_isPlayerOnPhone :count : true :_view: _isAI: [_handCards count]];
newCenter = [tempCard getButton].center;
}
_animationStillRunning = true;
if(![[DealViewController getCardsInPlayArray] containsObject:card] ) {
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{[card getButton].center = newCenter;} completion:^(BOOL finished){[self animationEnd];}];
while (_animationStillRunning){ //endAnimation will set _animationStillRunning to false when called
//stuck in here after first UIButton when threading code is in play
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
} //endAnimation will set _animationStillRunning to false when called
}
count++;
}
}
When i comment out the threading code each UIButton (Card) will animate one after another.
With the threading code is in play the first UIButton will animate but during the second run through the for-loop, the code will be stuck in the while-loop, waiting for the animation to end. I'm guessing the second animation doesn't even start.
I also tried this for the threading code:
[player performSelectorInBackground:#selector(sortAndSlideHandCards) withObject:nil];
Same outcome
Anyone have any ideas why animateWithDuration doesn't like getting called in a loop when in a thread other than the main one?
You should just be able to kick off the animations you want from whatever UI action is performed. The UIView animateWith... methods return immediately, so you don't need to worry about waiting for them to complete.
If you have an unknown number of animations to kick off sequentially, use the delay parameter. Pseudocode:
NSTimeInterval delay = 0;
NSTimeInterval duration = 0.25;
for (UIView *view in viewsToAnimate)
{
[UIView animateWithDuration:duration delay:delay animations:^{ ... animations ...}];
delay += duration;
}
This will increase the delay for each successive animation, so it starts at the end of the previous one.

Animating an Array of UIViews

I have an array of UIViews which I animate with the following code:
for (int i = 0; i<(int)viewArray.count; i++) {
UIView *view = [viewArray objectAtIndex:i];
CALayer *layer = view.layer;
//animate the layer
}
My question is, is there any way to have a delay between every animation, so that, for example, it animates one UIView in the array and the next animation starts after 0.2 seconds or so? Any help would be greatly appreciated!
You may want to try the following
float timeDelay = 0.2
float duration = YOUR_DURATION
for (int i = 0; i<(int)viewArray.count; i++) {
UIView *view = [viewArray objectAtIndex:i];
//animate the layer
[UIView animateWithDuration:duration delay:(i+1)*timeDelay
options: {Check UIView Class Reference on developer.apple.com}
animations:^{
[view.layer fantastic animation here];
} completion^(BOOL finised){
if(finished){
//Leave blank if nothing, good practice to implement debuging here or post animation processes here (like removing a subview from a super)
}
}];
}
Keep in mind that if u send your program the the background it may break your animations so make sure u recall this method in ur app Delegates:
- (void)applicationWillEnterForeground:(UIApplication *)application;
Hope it helps
Use this guy:
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
use the second parameter. Inside your loop, call that function, passing an every-increasing number to that parameter.

Objective-C: Issue with CGRect .frame intersect/contains

I have two UIImageViews located about center of a horizontal screen and when the user clicks a button another UIImageView, located off screen, slides in from the right. I'm am just trying to detect when the view being brought onto the screen collides with the two static views. The problem is when I run my code and check the CGRect frames they are returned from where the views start, not end, regardless of where I place the frame calls in my method or even if I place them outside the method in a separate one. I'm a bit new to Obj-C and I understand that Core Animation runs on a separate thread and I am guessing that is why I am getting the starting values. (Correct me if I am wrong here).
So, I guess the question here is how do I detect collisions when one item is static and another is animated. Here's my code (feel free to clean it up):
- (IBAction)movegirl
{
//disabling button
self.movegirlbtn.enabled = NO;
//getting graphics center points
CGPoint pos = bushidogirl.center;
CGPoint box1 = topbox.center;
CGPoint box2 = bottombox.center;
//firing up animation
[UIView beginAnimations: nil context: NULL];
//setting animation speed
[UIView setAnimationDuration:2.0 ];
//setting final position of bushidogirl, then resetting her center position
pos.x = 260;
bushidogirl.center = pos;
//running animations
[UIView commitAnimations];
//playing gong sound
[self.audioplayer play];
//enabling button
self.movegirlbtn.enabled = YES;
[self collisiondetection: bushidogirl : topbox];
}
- (void)collisiondetection:(UIImageView *)item1 : (UIImageView *)item2
{
CGRect item1frame = item1.frame;
CGRect item2frame = item2.frame;
NSLog(#"Item1 frame = %d and Item 2 frame = %d", item1frame, item2frame);
}
How about:
if (CGRectIntersectsRect(item1frame, item2frame))
{
// collision!!
}