iOS 7 parallax effect in my view controller
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:
- NGAParallaxMotion (requires iOS 7).
- DVParallaxView (requires iOS 5.0 or higher and ARC).
- MKParallaxView (tested with iOS 6.0, requires ARC).
- UIView-MWParallax (tested with iOS 6.1, requires ARC).
xcode/ios7: parallax effect with image clipped inside view
I think a better approach here might just be to use 2 separate views.
- The wrapping
UIView
you have now, sized to what you want with a corner radius andclipsToBounds
set toYES
. - a
UIImageView
with the image you want, added as a subview of theUIView
.
Then, just apply the motion effect to the image view. This will keep the containing view static and achieve the keyhole effect you are looking for.
Moving an image with ios 7 parallax effect
Update:
Just found a very nice 3rd party library for achieving this, it's called CRMotionView, it works very smooth and you can modify a lot of things.
here is the github link: https://github.com/chroman/CRMotionView
==================================================================================
i was thinking the same parallax when i first saw Facebook paper app. but after play with my code for a little bit, i don't think parallax is what we looking for. I could be wrong, but i went to do it all from the base, gyro motion manager. Here is my sample code:
//import the motion manager frame work first
#import <CoreMotion/CoreMotion.h>
//then need to add a motionManager
@property (strong, nonatomic) CMMotionManager *motionManager;
//you can paste all those codes in view did load
//i added a scroll view on the view controller nib file
self.mainScrollView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
//we don't want it to bounce at each end of the image
self.mainScrollView.bounces = NO;
//and we don't want to allow user scrolling in this case
self.mainScrollView.userInteractionEnabled = NO;
//set up the image view
UIImage *image= [UIImage imageNamed:@"YOUR_IMAGE_NAME"];
UIImageView *movingImageView = [[UIImageView alloc]initWithImage:image];
[self.mainScrollView addSubview:movingImageView];
//set up the content size based on the image size
//in facebook paper case, vertical rotation doesn't do anything
//so we dont have to set up the content size height
self.mainScrollView.contentSize = CGSizeMake(movingImageView.frame.size.width, self.mainScrollView.frame.size.height);
//center the image at intial
self.mainScrollView.contentOffset = CGPointMake((self.mainScrollView.contentSize.width - self.view.frame.size.width) / 2, 0);
//inital the motionManager and detec the Gyroscrope for every 1/60 second
//the interval may not need to be that fast
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.gyroUpdateInterval = 1/60;
//this is how fast the image should move when rotate the device, the larger the number, the less the roation required.
CGFloat motionMovingRate = 4;
//get the max and min offset x value
int maxXOffset = self.mainScrollView.contentSize.width - self.mainScrollView.frame.size.width;
int minXOffset = 0;
[self.motionManager startGyroUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:^(CMGyroData *gyroData, NSError *error) {
//since our hands are not prefectly steady
//so it will always have small rotation rate between 0.01 - 0.05
//i am ignoring if the rotation rate is less then 0.1
//if you want this to be more sensitive, lower the value here
if (fabs(gyroData.rotationRate.y) >= 0.1) {
CGFloat targetX = self.mainScrollView.contentOffset.x - gyroData.rotationRate.y * motionMovingRate;
//check if the target x is less than min or larger than max
//if do, use min or max
if(targetX > maxXOffset)
targetX = maxXOffset;
else if (targetX < minXOffset)
targetX = minXOffset;
//set up the content off
self.mainScrollView.contentOffset = CGPointMake(targetX, 0);
}
}];
I tested this on my device, worked pretty similar like facebook new app.
However, this is just an example code i wrote in half hour, may not be 100% accurate, but hope this would give you some ideas.
Parallax effect with UIMotionEffect not working
In the code that you have shared, one thing that I noticed is that you are using capital T
in the enums .TiltAlongHorizontalAxis
and .TiltAlongVerticalAxis
, which don't compile (at least on Xcode 11.4.1) -> those need to be changed to lowercase T
.
Apple's documentation for those enums is here.
Other than that, I don't see any problems with the code that you shared. In fact I created a sample project with a simple imageView
in the view to which I added constraints so that it fills up the entire space of its superview.
I then added the motion effects code that you shared, and ran it on an actual device (iPad Pro). It worked perfectly.
Could you check a few things that might be a source of the problem:
- Double check that
Settings
>Accessibility
>Motion
>Reduce Motion
is not turned on. Because if it is turned on, thenUIInterpolatingMotionEffect
won't work. - Test that the accelerometer of the device that you are testing on actually works. Or better still, use another device to test it. (This won't work on the simulator).
- If you are editing the frame of the
imageView
in code anywhere (or especially inviewWillLayout
/viewDidLayout
), you shouldn't be doing that.
In any case, if none of these things help you to fix your problem, can you create a simple sample project which demonstrates the bug and post it on Github? I can try to help if I can actually reproduce the problem.
Related Topics
Different Font Size for Different Devices in Xcode 6
Mobile Safari Position:Fixed Z-Index Glitch When Scrolling
Redirect Nslog to File in Swift Not Working
How to Add a Container View Programmatically
When Do I Need to Call Setneedsdisplay in iOS
Refresh Uipageviewcontroller - Reorder Pages and Add New Pages
Uicollectionview Update a Single Cell
How to Verify That I am Running on a Given Gcd Queue Without Using Dispatch_Get_Current_Queue()
How to Consecutively Present Two Alert Views Using Swiftui
Webkit Overflow Scrolling Touch CSS Bug on iPad
Downloading Uiimage via Alamofireimage
How to Make iPhone Vibrate Using Swift
What #Defines Are Set Up by Xcode When Compiling for Iphone
Coredata Get Distinct Values of Attribute