Here is a skeleton ... then you can get more detail at the suggested docs. This is real simple-minded but has a randomization and you can instantiate with your own image. I created an image container as a UIViewController and in my main (either your AppDelegate or the RootViewController) you create the instance in viewDidLoad. The methods that have to be overloaded in your AppDelegate, (the code is at the end of the post) so it receives shakes, are:
- viewWillAppear
- viewDidAppear
- viewWillDisappear
- viewDidDisappear
- canBecomeFirstResponder
- becomeFirstResponder
- nextResponder
You become a responder in viewWillAppear and you have to enable the device notifications (verified that there is a documented bug) so the motion methods get invoked.
- motionBegan:withEvent
- motionEnded:withEvent
I like to put an NSLog(@"%s", FUNCTION); statement in all my first-time written methods, that way I can quickly determine if something is missing to get my events, properly initialized, etc. Its just my style.
AnimatedImage.h isA ViewController
@interface AnimatedImage : UIViewController {
UIImageView *image;
UILabel *label;
float cx;
float cy;
float duration;
float repeat;
}
@property (nonatomic, retain) UIImageView *image;
@property (nonatomic, retain) UILabel *label;
-(id) initWithImageName: (NSString*) imageName;
-(IBAction) shake;
AnimatedImage.m
#include <stdlib.h>
#define random() (arc4random() % ((unsigned)RAND_MAX + 1))
#import "AnimatedImage.h"
@implementation AnimatedImage
@synthesize image;
@synthesize label;
-(id) initWithImageName: (NSString*) imageName {
NSLog(@"%s", __FUNCTION__);
image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
return self;
}
-(IBAction) shake {
cx = 0.90 + (random() % 10) / 100; // cx = 0.95;
cy = 0.90 + (random() % 10) / 100; // cy = 0.95;
duration = ( 5.0 + random() % 10) / 1000.0;
repeat = (65.0 + random() % 20);
image.transform = CGAffineTransformScale(CGAffineTransformIdentity, cx, cy);
[UIView beginAnimations: @"identifier" context: @"shake"];
[UIView setAnimationDelegate:image.superclass];
[UIView setAnimationDuration: duration]; // 0.008];
[UIView setAnimationRepeatCount: repeat]; // 85.0];
[UIView setAnimationRepeatAutoreverses: YES];
image.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
[UIView commitAnimations];
}
-(IBAction) bounce {
image.transform = CGAffineTransformTranslate(image.transform, -20, -6);
[UIView beginAnimations: @"identifier" context: @"bounce"];
[UIView setAnimationDelegate:image.superclass];
[UIView setAnimationDuration: duration];
[UIView setAnimationRepeatCount: repeat];
[UIView setAnimationRepeatAutoreverses: YES];
image.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.0, 1.0);
[UIView commitAnimations];
}
@end
This "root or main" delegate is a UIViewController. I have left of detail to show its simplicity, but still left my 'trace' code that I find essential for first-time debugging. Also, as an aside, I believe you have to overload motionBegan, even if you only need the motionEnded event. I recall that my app did not work and then I added the motionBegan and it started to call motionEnded. It kinda' makes sense that it would be that way, but I don't have any documentation to back it up. A simple experiment after you get it working can confirm or deny my comment.
- (void)viewDidLoad {
[super viewDidLoad];
[super becomeFirstResponder];
.
.
.
NSLog(@"%s", __FUNCTION__);
NSLog(@"%s: I %s first responder! (%@)", __FUNCTION__, [self isFirstResponder] ? "am" : "am not", self);
.
.<created image in this ViewController's NIB using IB>
.
someImage = (UIImageView *) [self.view viewWithTag:TAG_SOMEIMAGE];
aniImage = [[AnimatedImage alloc] init];
aniImage.image = someImage;
}
-(void) viewWillAppear: (BOOL) animated{
NSLog(@"%s", __FUNCTION__);
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(receivedRotate:)
name: UIDeviceOrientationDidChangeNotification
object: nil];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%s", __FUNCTION__);
[super becomeFirstResponder];
assert( [self canPerformAction:@selector(motionEnded:withEvent:) withSender:self] );
}
- (void)viewWillDisappear:(BOOL)animated {
NSLog(@"%s", __FUNCTION__);
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver: self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
NSLog(@"%s", __FUNCTION__);
[super resignFirstResponder];
}
- (BOOL)canBecomeFirstResponder {
NSLog(@"%s", __FUNCTION__);
return YES;
}
- (BOOL)becomeFirstResponder {
NSLog(@"%s", __FUNCTION__);
return YES;
}
- (UIResponder *)nextResponder {
NSLog(@"%s", __FUNCTION__);
return [self.view superview];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(@"%s", __FUNCTION__);
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(@"%s", __FUNCTION__);
if( [self isFirstResponder] ) {
[aniImage shake];
}
}
This code does work -- but if I snipped out too much detail, just ask and I will make certain to add enough to help you along. This was a really rewarding task for me and I am excited to share it with someone looking for some help in the same experience.