iOS 7 parallax effect in my view controller

64,318

Solution 1

With iOS 7, Apple introduced UIMotionEffect to add Motion effects that are related to the orientation of the user’s device. For example, to emulate the parallax effect on the home screen, you can use the subclass UIInterpolatingMotionEffect, as explained here, just with a few lines of code.

Objective-C:

// Set vertical effect
UIInterpolatingMotionEffect *verticalMotionEffect = 
  [[UIInterpolatingMotionEffect alloc] 
  initWithKeyPath:@"center.y"
             type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
verticalMotionEffect.minimumRelativeValue = @(-10);
verticalMotionEffect.maximumRelativeValue = @(10);

// Set horizontal effect 
UIInterpolatingMotionEffect *horizontalMotionEffect = 
  [[UIInterpolatingMotionEffect alloc] 
  initWithKeyPath:@"center.x"     
             type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
horizontalMotionEffect.minimumRelativeValue = @(-10);
horizontalMotionEffect.maximumRelativeValue = @(10);
  
// Create group to combine both
UIMotionEffectGroup *group = [UIMotionEffectGroup new];
group.motionEffects = @[horizontalMotionEffect, verticalMotionEffect];

// Add both effects to your view
[myBackgroundView addMotionEffect:group];

Swift (Thanks to @Lucas):

// Set vertical effect
let verticalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y",
type: .TiltAlongVerticalAxis)
verticalMotionEffect.minimumRelativeValue = -10
verticalMotionEffect.maximumRelativeValue = 10

// Set horizontal effect
let horizontalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x",
    type: .TiltAlongHorizontalAxis)
horizontalMotionEffect.minimumRelativeValue = -10
horizontalMotionEffect.maximumRelativeValue = 10

// Create group to combine both
let group = UIMotionEffectGroup()
group.motionEffects = [horizontalMotionEffect, verticalMotionEffect]

// Add both effects to your view
myBackgroundView.addMotionEffect(group)

Also, you can find a bunch of libraries to do this easier or to add this functionality to older iOS versions:

Solution 2

Translated to swift in case anyone is lazy. Please vote @veducm answer up if you found this useful

@IBOutlet var background : UIImageView!

func parallaxEffectOnBackground() {
    let relativeMotionValue = 50
    var verticalMotionEffect : UIInterpolatingMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y",
        type: .TiltAlongVerticalAxis)
    verticalMotionEffect.minimumRelativeValue = -relativeMotionValue
    verticalMotionEffect.maximumRelativeValue = relativeMotionValue

    var horizontalMotionEffect : UIInterpolatingMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x",
        type: .TiltAlongHorizontalAxis)
    horizontalMotionEffect.minimumRelativeValue = -relativeMotionValue
    horizontalMotionEffect.maximumRelativeValue = relativeMotionValue

    var group : UIMotionEffectGroup = UIMotionEffectGroup()
    group.motionEffects = [horizontalMotionEffect, verticalMotionEffect]

    self.background.addMotionEffect(group)
}

Solution 3

@veducm's solution can be a little shorter. The UIMotionEffectGroup for its x and y motion is obsolete if you add the the x and y-axis motionEffects separately.

UIInterpolatingMotionEffect *motionEffect;
motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x"
                                                               type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[bgView addMotionEffect:motionEffect];

motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y"
                                                               type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[bgView addMotionEffect:motionEffect];

Solution 4

const static CGFloat kCustomIOS7MotionEffectExtent = 10.0; 

- (void)applyMotionEffects:(UIView *YOUR_VIEW) {
     if (NSClassFromString(@"UIInterpolatingMotionEffect")) {
         UIInterpolatingMotionEffect *horizontalEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x"
                                                                                                        type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
         horizontalEffect.minimumRelativeValue = @(-kCustomIOS7MotionEffectExtent);
         horizontalEffect.maximumRelativeValue = @( kCustomIOS7MotionEffectExtent);
         UIInterpolatingMotionEffect *verticalEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y"
                                                                                                      type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
         verticalEffect.minimumRelativeValue = @(-kCustomIOS7MotionEffectExtent);
         verticalEffect.maximumRelativeValue = @( kCustomIOS7MotionEffectExtent);
         UIMotionEffectGroup *motionEffectGroup = [[UIMotionEffectGroup alloc] init];
         motionEffectGroup.motionEffects = @[horizontalEffect, verticalEffect]; 
         [YOUR_VIEW addMotionEffect:motionEffectGroup];
     }
}

Solution 5

Here is an easy category to integrate the effect on iOs7+ :

NSString *const centerX = @"center.x";
NSString *const centerY = @"center.y";

//#define IS_OS_7_OR_LATER    ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)

@implementation UIView (TLMotionEffect)

- (void)addCenterMotionEffectsXYWithOffset:(CGFloat)offset {

//    if(!IS_OS_7_OR_LATER) return;
    if(floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) return;

    UIInterpolatingMotionEffect *xAxis;
    UIInterpolatingMotionEffect *yAxis;

    xAxis = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:centerX type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
    xAxis.maximumRelativeValue = @(offset);
    xAxis.minimumRelativeValue = @(-offset);

    yAxis = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:centerY type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
    yAxis.minimumRelativeValue = @(-offset);
    yAxis.maximumRelativeValue = @(offset);

    UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
    group.motionEffects = @[xAxis, yAxis];

    [self addMotionEffect:group];
}

@end

https://github.com/jvenegas/TLMotionEffect

Share:
64,318

Related videos on Youtube

Dan Fabulich
Author by

Dan Fabulich

Updated on October 10, 2020

Comments

  • Dan Fabulich
    Dan Fabulich over 3 years

    I'm developing an app for iOS 7 in Objective-C. I've got a screen in my app with a few buttons and a pretty background image. (It's a simple xib with UIButtons on top of a UIImageView.)

    I was thinking that it'd be cool if those buttons had the parallax effect that the iOS 7 home screen has, so if you tilt the phone you could see the background.

    How can I implement that effect in my own app?

  • Dan Fabulich
    Dan Fabulich over 10 years
    NGAParallaxMotion is for iOS 7+, not for older iOS versions. It provides a category for UIView, allowing you to set a .parallaxIntensity value, setting up the UIMotionEffect parameters with one line of code.
  • veducm
    veducm over 10 years
    Thanks! I have updated the answer to include supported versions and requirements.
  • gstroup
    gstroup over 10 years
    Actually, you want to add the motion effect to the background view, not the front view. But other than that, this code works great! Thanks.
  • veducm
    veducm over 10 years
    Thanks @gstroup! I have updated the code to be 'myBackgroundView'.
  • Eric
    Eric over 10 years
    Does anyone know how to check if the user has disabled the effects? stackoverflow.com/questions/19132000/…
  • Rubberduck
    Rubberduck over 10 years
    @gstroup Actually, on the home screen both the foreground and the background are moving. But if you were to chose only one layer to apply the parallax effect to, I agree the background would probably be best.
  • dcone
    dcone almost 10 years
    @veducm: What about a parallax pinch zoom effect, how will this be done?
  • veducm
    veducm almost 10 years
    @dcone: not sure this is what you are looking for. Otherwise, could you please give an example to understand what you want?
  • Flexicoder
    Flexicoder almost 10 years
    Just as a heads up if anyone is using this shortened version, the first motion effect needs to have a type of UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis and not UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis as it is in the code laid out
  • damithH
    damithH over 9 years
    @Rob according to your suggestion updated the answer
  • Maciej Swic
    Maciej Swic over 9 years
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.
  • Bejil
    Bejil over 9 years
    It will be hard to integrate the whole github project here
  • Maciej Swic
    Maciej Swic over 9 years
    You don't have to bring the whole project, only the essential part like i demonstrated by editing the answer. This is so that the code sticks around in case Github is down, you pull the repo or the sky falls down.
  • veducm
    veducm over 9 years
    Thanks @Lucas! I have updated my answer to include the swift sample too. It is based on this one. Feel free to review it and do any changes needed.
  • Daniel Saidi
    Daniel Saidi over 9 years
    This worked right away, in a way that was exactly what I was after. Thank you so much!
  • Ryan
    Ryan about 9 years
    An important configuration that is necessary to think about for anyone using parallax, is that you need to consider the Z-axis layering of the view. If the view is to look "above", you will have a minimumRelativeValue less than the maximumRelativeValue. If the view is to look "behind" (such as iOS home/lock screen), then the values are to be reversed.
  • GilesDMiddleton
    GilesDMiddleton about 9 years
    Any ideas how to apply this so it just moves a single sprite kit SKSpriteNode inside an SKScene, rather than a whole view moving?
  • David Lari
    David Lari about 8 years
    @BooHoo Adding them as a group is obsolete? Where do you see that? As of the time of this writing the Apple docs don't have anything about it being obsolete. The whole purpose of grouping them is to improve performance. All effects in the group are rendered once. When you apply them separately, they must be rendered separately. Feel free to correct me if I'm wrong (and point me to Apple's documentation on it.)