UIMotionEffects for Dummies

By: Conor 5. October 2013 13:30
The Hippy Hippy Shake 70X50cm digital print 2008


Things I don't heart!

You know what I hate? I hate superfluous code, I hate repetitive code and I hate needing to write and remember blocks of code for something that should really be a simple one liner. During weekly code reviews at @redwindsoft I constantly try to drive home the need for simplicity and easy to understand code.


Things I heart!

I fucking heart Objective-C categories.


The Hippy Hippy Shake!

So, one of the coolest and almost certain to be overused features of iOS 7 is the parallax effect that you see on your Springboard now, giving the impression that different layers are moving on different plains independent of each other, in response to the movement of your device. In layman's terms, it's the nifty looking movement your app icons make when you move your iPhone or iPad around.


We've implemented a shed load of this effect in an app that will be released in the next few weeks. Unfortunately I can't really say much about the app in question but @sobei_'s tweet a couple of days ago might give you some idea of what it is…


All the ones and zeros!

Anyways, back to the issue at hand. Let's say I have a single UIView that I would like to apply motion effects to. It's pretty simple really. For now, let's assume I just want to move in one direction, sideways.


We build an UIInterpolatingMotionEffect, give it a direction, minimum and maximum value and then add it to the view.


	// Define two offsets, these are how much your view will move. 
	// Bigger = more movement. There's no one "right" value, experiment and have fun. 
	// But be aware for backgrounds and full screen images the bigger you go you may need to increase the size of the actual image itself.
	CGFloat minimum = -100.0f; 
	CGFloat maximum = 100.0f; 

	// Create an *Interpolating* Motion effect and tell it to work off an object's center.x key path, tilting along the horizontal axis. 
	UIInterpolatingMotionEffect *motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];

	// set the minimum and maximum relative values using our offsets
	motionEffect.minimumRelativeValue = @(minimum);
	motionEffect.maximumRelativeValue = @(maximum);

	// finally, add the effect to the desired view
	[view addMotionEffect:motionEffect];


Already to me, that code is over-complicated for what we want to achieve, and we've only added one axis. Let's add a second, the vertical axis.


	CGFloat horizontalMinimum = -100.0f; 
	CGFloat horizontalMaximum = 100.0f; 

	UIInterpolatingMotionEffect *horizontal = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
	horizontal.minimumRelativeValue = @(horizontalMinimum);
	horizontal.maximumRelativeValue = @(horizontalMaximum);
	CGFloat verticalMinimum = -100.0f; 
	CGFloat verticalMaximum = 100.0f; 

	UIInterpolatingMotionEffect *vertical = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
	vertical.minimumRelativeValue = @(verticalMinimum);
	vertical.maximumRelativeValue = @(verticalMaximum);
	// this time we need to create a motion effects group and add both of our motion effects to it. 
	UIMotionEffectGroup *motionEffects = [[UIMotionEffectGroup alloc] init];
	motionEffects.motionEffects = @[horizontal, vertical];
	// add the motion effects group to our view
	[view addMotionEffect:motionEffects];


So, pretty much over 10 lines of code to add the effect to a single view. Really, most of it is bollox too. Turns out, for us anyway, we always wanted to apply equal offsets to the horizontal and vertical paths. And the minimum/maximum values that worked the best were always just the same but in opposite directions.


As I said, we are applying this all over the shop in our upcoming app. As you might expect, we have way more than a single view in every scene the user will experience. Imagine how ugly and repetitive our code base would be if we were repeating this code every time we wanted to add motion to a single view!



Wouldn't it be so much nicer and easier to be able to just tell each view to add motion effects with a particular offset.


	[view addMotionEffects:20.0f];


This sort of functionality is where Objective-C categories really come into their own. We simply created a new Category on UIView called UIView+Motion and gave it the ability to add and remove motion effects, imported it into parent classes and BAM, every interface element in our entire app can now use motion effects in a single line. Sexy.


One other thing we wanted to achieve was consistency from scene to scene between what offsets were used. We limited our different offsets to 3 conceptual layers that every scene should obey
  • Background
  • Content
  • Interface


and here's the values that worked really well for us

	#define BACKGROUND_LAYER -25.0
	#define CONTENT_LAYER 75.0
	#define INTERFACE_LAYER 25.0



So now each view, when setup can easily and consistently apply the correct motion effects for it's layer. For example:


	[self.background addMotionEffects:BACKGROUND_LAYER];
	[self.content addMotionEffects:CONTENT_LAYER];
	[self.buttonTray addMotionEffects:INTERFACE_LAYER];
	[self.numberBadge addMotionEffects:INTERFACE_LAYER];
	[self.textOverlay addMotionEffects:INTERFACE_LAYER];


You can download the Category here, (complete with top secret project code name in the header comments): UIView+Motion.zip
TweetMe created by sexyselect.net/blog


blog comments powered by Disqus