I want to create an animated menu.
first it's hidden outside screen
when user taps the screen, it will slide in
user can tap it and it do something
then after 3s it will slide outside screen
But problem is it not response when I tap it. Why?
example code: All code is in my UIView class.
showing = NO;
box = [[UIView alloc] initWithFrame:CGRectMake(500,-200,200,200)];
box.backgroundColor = [UIColor redColor];
UITapGestureRecognizer *tapBox = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapMenuHandler:)] autorelease];
[box addGestureRecognizer:tapBox];
[self addSubView:box];
and:
-(void) tapMenuHandler: (UIView *)obj {
//Do something
NSLog(#"tap box");
}
and:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (!showing) {
box.frame = CGRectMake(500, -200, 200, 200);
[UIView animateWithDuration:0.5f delay:0.3f options:(UIViewAnimationOptionAllowUserInteraction) animations:^{
box.frame = CGRectMake(500, 200, 200, 200);
} completion:^(BOOL finished) {
showing = YES;
[UIView animateWithDuration:0.5f delay:3.0f options:(UIViewAnimationOptionAllowUserInteraction) animations:^{
box.frame = CGRectMake(500, -200, 200,200);
} completion:^(BOOL finished) {
showing = NO;
}];
}];
}
}
Thank for your help and sorry for my English :)
Edit: more detail
I tried set start my box's origin in screen (ex. 500,600), It still always response when tap at start point (500,600) even though my box move to another position.
Update:
I changed my way to move box outside screen by use NSTimer then it's work!
[UIView animateWithDuration:0.5f delay:0.3f options:UIViewAnimationOptionAllowUserInteraction animations:^{
box.frame = CGRectMake(500,200,200,200);
} completion:^(BOOL finished) {
isShowMenubar = YES;
//use NSTimer instead of delay
[NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:#selector(hideBox) userInfo:nil repeats:NO];
}];
and
-(void)hideBox {
[UIView animateWithDuration:0.5f delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{
box.frame = CGRectMake(500,-200,200,200);} completion:^(BOOL finished) {
isShowMenubar = NO;
}];
}
The reason why the box is not receiving touches has to do with how animation works. Your box is actually offscreen, but visually, it's going through the animation.
Even though you set the delay as 3.0, it still moves the box beforehand (due to the animation method still being called). So in your current code, if you tapped on the box at (500, -200) then it will receive the touch.
To fix this, either use an NSTimer or Grand Central Dispatch to delay.
NSTimer:
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self action:#selector(dismissBox) userInfo:nil repeats:NO];
The problem with NSTimer is that you'll have to put that animation code in a separate method.
Grand Central Dispatch:
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[UIView animateWithDuration:0.5f animations:^{
box.frame = CGRectMake(box.frame.origin.x, -200, 200,200);
}completion:^(BOOL finished) {
showing = NO;
}];
});
I ran the code and didn't see the red view either. Then playing around for a bit, I got that your view is animating but is still out of bounds so you never see it. try changing the animation block to something like (the origin of the frame is at the left bottom corner)
box.frame = CGRectMake(100, 100, 200, 200);
and hopefully it will come animating.
Also, assuming that you run these in your view controller, the correct method should be
[self.view addSubview:box];
hope this helps
Related
I have an wheel image in .png format, I want to know how can i animate so that it rotates continuously, I searched on stackoverflow and found certain snippets of code which helped me rotate my image but it wouldn't rotate continuously, it would just rotate for a few seconds and stop, the code as follows
the code in viewdidload
UIImageView *imageToMove =
[[UIImageView alloc] initWithImage:[UIImageimageNamed:#"horo_circle.png"]];
[self.view addSubview:imageToMove];
[self rotateImage:imageToMove duration:5.0
curve:UIViewAnimationCurveEaseIn degrees:180];
and the animation
- (void)rotateImage:(UIImageView *)image duration:(NSTimeInterval)duration
curve:(int)curve degrees:(CGFloat)degrees
{
// Setup the animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve:curve];
[UIView setAnimationBeginsFromCurrentState:YES];
// The transform matrix
CGAffineTransform transform =
CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));
image.transform = transform;
// Commit the changes
[UIView commitAnimations];
}
and the following lines after the import
#define M_PI 3.14159265358979323846264338327950288 /* pi */
#define DEGREES_TO_RADIANS(angle) (angle / 180.0 * M_PI)
You are better of doing this with a CABasicAnimation:
if ([self.spinnerOverlay animationForKey:#"SpinAnimation"] == nil) {
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
animation.duration = 10.0f;
animation.repeatCount = INFINITY;
[self.spinnerOverlay.layer addAnimation:animation forKey:#"SpinAnimation"];
}
In this code I check whether the animation is all ready set, not need to set it again.
The spinnerOverlay is in your case the UIImageView you want to rotate.
To stop the animation:
[self.spinnerOverlay.layer removeAnimationForKey:#"SpinAnimation"];
The code bellow will spin a UIImageView called iconView continiously until rotate == NO.
The image will always return to its original position.
- (void)performRotationAnimated
{
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.iconView.transform = CGAffineTransformMakeRotation(M_PI);
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.iconView.transform = CGAffineTransformMakeRotation(0);
}
completion:^(BOOL finished){
if (_rotate) {
[self performRotationAnimated];
}
}];
}];
}
This is a variation on the same theme.
(Total duration: 2 * 0.5 = 1 sec for a spin)
Works like magic!
Here is another way to do it with a timer.
In your viewController.h file, you'll need to declare your timer and image:
#interface ViewController : UIViewController
{
IBOutlet UIImageView *spinningImage;
NSTimer *spinTimer;
}
Next, in your ViewController.m, you add this code and can make the spin go faster by lowering the time interval or by increasing the transform interval.
-(void) viewDidLoad {
spinTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target: self selector:#selector(spinVoid) userInfo:nil repeats:YES];
}
-(void) spinVoid {
spinningImage.transform = CGAffineTransformRotate(spinningImage.transform, 0.001);
}
Hope this helps!
Now that we're at iOS6, please consider switching to block based animations (that are available since iOS 4.0) rather than the classical method. Try this:
[UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionRepeat animations:^{
image.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));
} completion:nil];
Using the method you've chosen for animations :
[UIView setAnimationRepeatCount:10000000];
or, you can specify the UIViewAnimationOptionRepeat flag in the blocks method animateWithDuration:delay:options:animations:completion:
I'm trying to add a short delay to my app. It's just the splash screen fade out so it won't affect any other functionality in the app (since nothing else is running). I've tried a variety of approaches with no success. (This is happening in viewDidLoad of a viewController):
C's sleep:
...
//add the splash screen
[self.view addSubview:splashScreen];
sleep(3);
[self fadeOut:splashScreen];
NSObject's performSelector (thought this would work because doesn't UIViewController inherit from NSObject?)
[self performSelector:#selector(fadeOut:) afterDelay:3];
NSTimeInterval:
//wait 3 seconds
NSTimeInterval theTimeInterval = 3;
[self fadeOut:splashScreen withADelayOf:&theTimeInterval];
Here is fadeOut (written to work with the NSTimeInterval example)
- (void) fadeOut:(UIView *)viewToToggle withADelayOf:(NSTimeInterval* ) animDelay {
[UIView setAnimationDelay:*animDelay];
[UIView animateWithDuration:0.25 animations:^{
viewToToggle.alpha = 0.0;
}];
}
I get the fadeOut but not the delay. Can someone nudge me in the right direction. Thanks.
you can try dispatch_after or animateWithDuration:delay:options:animations:completion:
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//yourcode
});
or
[UIView animateWithDuration:0.175 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
//your code
}completion:^(BOOL completed){
//animation completion execution
}];
You should rely on this method if you want to animate some properties of your view with some delay:
+(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Have a look to the ref doc here.
So your code could be something like this:
[UIView animateWithDuration:0.25 delay:3.0 options: UIViewAnimationOptionCurveLinear animations:^{
viewToToggle.alpha = 0.0;
} completion: nil];
Tiguero and J2TheC both pointed me in the exact direction I needed to go:
Here's the code I used in case someone else needs the assist:
//add the splash screen
[self.view addSubview:splashScreen];
//fade it out with a delay
[UIView animateWithDuration:0.75 delay:3.0 options:UIViewAnimationOptionCurveEaseIn
animations:^{
splashScreen.alpha = 0.0;
}
completion:^ (BOOL finished) {
//do something on end
}];
Do One thing:
Make an uiimageview ref in the Appdelegate and in the didfinishLunching do this:
// Dont assign the main viewcontroller to the windwo here.
{
img = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"splash.png"]];
img.frame = CGRectMake(0, 0, 320, 480);
[self.window addSubview:img];
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(removeSplash) userInfo:nil repeats:NO];
[self.window makeKeyAndVisible];
}
- (void)removeSplash{
[img removerFromSuperview];
self.window.rootViewController = self.viewController;
}
I'm working on a page-based iPhone app using Xcode 4.4 with ARC and have been stuck on this for awhile. On a certain page, a UIImageView finger needs to slide up and point to something and then slide left off the screen. This works as expected when I run it once, but when I turn the page and go back - there is now 2 fingers sliding about 0.5 seconds out of sync with eachother.
Here's the code:
-(void)fingerSwipeUp:(UIImageView *)imageView
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[imageView setCenter:CGPointMake(213.75,355.5)];
[UIView commitAnimations];
}
-(void)fingerSwipeLeft:(UIImageView *)imageView
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[imageView setCenter:CGPointMake(-80,355.5)];
[UIView commitAnimations];
}
UIImageView *finger = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"finger.png"]];
// position finger off the screen
[finger setFrame:CGRectMake(142.5,480,152.5,243)];
[self.view addSubview:finger];
[self performSelector:#selector(fingerSwipeUp:) withObject:finger afterDelay:6];
[self performSelector:#selector(fingerSwipeLeft:) withObject:finger afterDelay:8];
Any help greatly appreciated
I'm guessing that you're creating the "finger" view in ViewDidAppear()? If that's the case, every time the page is turned (hidden) and then gone back to, you're adding another arrow (subview). What about this instead:
if (finger == nil) {
finger = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"finger.png"]];
[finger setFrame:CGRectMake(142.5,480,152.5,243)];
[self.view addSubview:finger];
}
[self performSelector:#selector(fingerSwipeUp:) withObject:finger afterDelay:6];
[self performSelector:#selector(fingerSwipeLeft:) withObject:finger afterDelay:8];
If you've got a number of animations in a sequence, you might find it easier to use the block-based UIView animation syntax:
[UIView animateWithDuration:1.0 delay:6.0 options:UIViewAnimationCurveEaseInOut animations:^{
// animation 1
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 delay:8.0 options:UIViewAnimationCurveEaseInOut animations:^{
// animation 2
}];
}];
This will only start animation 2 after animation 1 has finished, so you don't need to worry about allowing for the duration of animation 1 when delaying animation 2.
I'm trying to animate a view with some buttons and controllers for my main view into the screen from the right side of the screen. The problem is it doesn't animate, it just goes directly to the finished state.
This is my code:
UIStoryboard* storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
options = [storyBoard instantiateViewControllerWithIdentifier:#"OptionsView"];
options.delegate = self;
[self.view addSubview:options.view];
options.view.center = CGPointMake(floor(total_width+options.view.frame.size.width/2)+10, floor(total_height/2));
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationOptionCurveEaseOut
animations:^{
options.view.center = CGPointMake(floor(total_width-options.view.frame.size.width/2)+10, floor(total_height/2));
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
EDIT
To make things more strange, if I put this animation inside the completion block of another animation then this one works, but not the new one.
UIButton* boton = (UIButton*) sender;
[UIView animateWithDuration:5.0
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
boton.alpha = 0.0;
//Jumps directly to 0, animation doesn't work
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
options.view.center = CGPointMake(floor(total_width-options.view.frame.size.width/2)+10, floor(total_height/2));
//It works now, but it doesn't take 5 seconds to start, it starts right away
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}];
The line before the animateWithDuration:... call directly assigns the center to the same value that you're animating to. Either remove that or assign the initial center value there. An animation that has the same start and end values obviously has no effect.
I have a UIViewController that should control 2 UIImageViews to fade in.
I managed to animate them using the UIView animateWithDuration method, like this:
[UIView animateWithDuration:1.5
animations:^{
[mFrontpageTitle setAlpha:0];
[mFrontpageTitle setAlpha:1];
}
completion:^(BOOL finished){
[self AnimatePause];
}];
and
-(void)AnimatePause {
[UIView animateWithDuration:5.0
animations:^{
[mFrontpageTitle setAlpha:0.99];
[mFrontpageTitle setAlpha:1];
}
completion:^(BOOL finished){
[self AnimateAuthor];
}];
which fires the next animation. Now, creating a transition from .99 to 1.0 is not very clean.
Is there a better way to trigger a block or sending a message by just defining a duration?
thanks
Zuppa
NSTimeInterval delay = 1.5; //in seconds
[self performSelector:#selector(myMethod) withObject:nil afterDelay:delay];