A couple of reactions:
I might suggest using
gravity
rather thanattitude
, as it nicely represents the physical orientation of the device.Rather than using a string to hold the value, I might just update the numeric values.
Rather than all of those
if
statements, just have a formula that represents how the view should be placed.If you want to move a bunch of views, you should put them in a
UIView
(commonly called a "container view", not to be confused with the custom container control in IB of the same name) and then just move the container view.
So, if using autolayout, I might have the following properties:
@property (nonatomic, strong) CMMotionManager *motionManager;
@property CGFloat top;
@property CGFloat left;
And then I can update the autolayout constraints like so:
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 0.05f;
self.top = self.topConstraint.constant;
self.left = self.leadingConstraint.constant;
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
self.leadingConstraint.constant = self.left - motion.gravity.x / 8.0 * 100.0;
self.topConstraint.constant = self.top - motion.gravity.y / 8.0 * 100.0;
}];
Or if not using autolayout:
@property (nonatomic, strong) CMMotionManager *motionManager;
@property CGPoint originalCenter;
and
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 0.05f;
self.originalCenter = self.containerView.center;
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
self.containerView.center = CGPointMake(self.originalCenter.x - motion.gravity.x / 8.0 * 100.0, self.originalCenter.y - motion.gravity.y / 8.0 * 100.0);
}];
Frankly, having the motion manager operate in the main queue gives me the willies, so I might (a) create a NSOperationQueue
for the motion updates and only update some class property; and (b) use a CADisplayLink
(part of the QuartzCore.framework) to update the view (if needed). Thus:
@property (nonatomic, strong) CMMotionManager *motionManager;
@property (nonatomic, strong) NSOperationQueue *motionQueue;
@property (nonatomic) CMAcceleration gravity;
@property (nonatomic) CGPoint originalCenter;
@property (nonatomic) BOOL updatePosition;
@property (nonatomic, strong) NSMutableArray *angles;
@property (nonatomic, strong) CADisplayLink *displayLink;
and
- (void)viewDidDisappear:(BOOL)animated
{
[self stopDisplayLink];
[self.motionManager stopDeviceMotionUpdates];
self.motionManager = nil;
self.motionQueue = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 0.05f;
self.motionQueue = [[NSOperationQueue alloc] init];
self.motionQueue.name = [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".motion"];
self.originalCenter = self.containerView.center;
self.updatePosition = NO;
[self.motionManager startDeviceMotionUpdatesToQueue:self.motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
@synchronized(self) {
self.gravity = motion.gravity;
self.updatePosition = YES;
}
}];
[self startDisplayLink];
}
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
@synchronized(self) {
if (!self.updatePosition)
return;
self.containerView.center = CGPointMake(self.originalCenter.x - self.gravity.x / 8.0 * 100.0, self.originalCenter.y - self.gravity.y / 8.0 * 100.0);
self.updatePosition = NO;
}
}