Have you seen new Apple TV? Looks great, and brand new design brings some iOS/OSX feeling! As always, Apple included subtle, but very cool effect to interact with interface. Since iOS 7, you can see a parallax effect in springboard by rotating the phone. This time, Apple utilized this effect for icons in Apple TV:

I was wondering whether this effect is available to developers. Unfortunately, after short investigation, I didn’t find anything. 🙁 Well, this a great opportunity to create it by myself (and with other developers)!

The whole magic is inside this method:

func applyParallaxEffectOnView(basedOnTouch touch: UITouch?) { if let touch = touch, let superview = superview { let offsetX = (0.5 - touch.locationInView(superview).x / superview.bounds.width) * -1 let offsetY = (0.5 - touch.locationInView(superview).y / superview.bounds.height) * -1 var t = CATransform3DMakeScale(1.1, 1.1, 1.1) t.m34 = 1.0/(-500) let xAngle = (offsetX * parallaxOffsetDuringPick) * CGFloat(M_PI / 180.0) let yAngle = (offsetY * parallaxOffsetDuringPick) * CGFloat(M_PI / 180.0) t = CATransform3DRotate(t, xAngle, 0, -(0.5 - offsetY), 0) layer.transform = CATransform3DRotate(t, yAngle, (0.5 - offsetY) * 2, 0, 0) applyParallaxEffectOnSubviews(xOffset: offsetX, yOffset: offsetY) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 func applyParallaxEffectOnView ( basedOnTouch touch : UITouch ? ) { if let touch = touch , let superview = superview { let offsetX = ( 0.5 - touch . locationInView ( superview ) . x / superview . bounds . width ) * - 1 let offsetY = ( 0.5 - touch . locationInView ( superview ) . y / superview . bounds . height ) * - 1 var t = CATransform3DMakeScale ( 1.1 , 1.1 , 1.1 ) t . m34 = 1.0 / ( - 500 ) let xAngle = ( offsetX * parallaxOffsetDuringPick ) * CGFloat ( M_PI / 180 . 0 ) let yAngle = ( offsetY * parallaxOffsetDuringPick ) * CGFloat ( M_PI / 180 . 0 ) t = CATransform3DRotate ( t , xAngle , 0 , - ( 0.5 - offsetY ) , 0 ) layer . transform = CATransform3DRotate ( t , yAngle , ( 0.5 - offsetY ) * 2 , 0 , 0 ) applyParallaxEffectOnSubviews ( xOffset : offsetX , yOffset : offsetY ) } }

In short – take a position of touch and divide it by size of superview(and multiply by -1 to make a rotation effect in correct direction). Then, zoom a view a little bit and set small value for mysterious m34 variable. Finally, calculate angle for x and y and apply this to CATransform3DRotate. Same goes to subviews (applyParallaxEffectOnSubviews method applies this).

Whoa, the hardest part is done! Let’s go to other methods to reproduce parallax effect better. We have to keep in mind that all subviews moves independently, so setting size of main background image equal to parallax view makes no sense – quickly we would see leaks of default white background. Let’s fix this and slightly increase subviews:

func resizeSubviewsForParallax() { let offset: CGFloat = initialParallaxOffset contentView.subviews.forEach { subview in subview.frame.origin = CGPoint(x: -offset, y: -offset) subview.frame.size = CGSize(width: subview.frame.size.width + offset * 2.0, height: subview.frame.size.height + offset * 2.0) } } 1 2 3 4 5 6 7 func resizeSubviewsForParallax ( ) { let offset : CGFloat = initialParallaxOffset contentView . subviews . forEach { subview in subview . frame . origin = CGPoint ( x : - offset , y : - offset ) subview . frame . size = CGSize ( width : subview . frame . size . width + offset * 2.0 , height : subview . frame . size . height + offset * 2.0 ) } }

How to achieve this cool glow effect? Well, there’s more than one option, I chose, I suppose, the easiest one: UIImageView with glow .png image that follows touch and changes alpha. As you can see in gif from the beginning, it only shows at the bottom of view.

func applyGlowEffectOnView(basedOnTouch touch: UITouch?) { let changeAlphaValue: CGFloat = 0.05 if let touch = touch where touch.locationInView(self).y > bounds.height / 2 { glowEffect.center = touch.locationInView(self) applyGlowAlpha(glowEffect.alpha + changeAlphaValue) } else { applyGlowAlpha(glowEffect.alpha - changeAlphaValue) } } 1 2 3 4 5 6 7 8 9 func applyGlowEffectOnView ( basedOnTouch touch : UITouch ? ) { let changeAlphaValue : CGFloat = 0.05 if let touch = touch where touch . locationInView ( self ) . y > bounds . height / 2 { glowEffect . center = touch . locationInView ( self ) applyGlowAlpha ( glowEffect . alpha + changeAlphaValue ) } else { applyGlowAlpha ( glowEffect . alpha - changeAlphaValue ) } }

applyGlowAlpha() takes care of changing alpha(do not reduce the value under 0.0 and above 1.0).

That’s pretty much it, and here’s the final effect!

If you’re interested in adding this effect to your app, here’s the repo on GitHub! Feel free to modify and enhancing it by adding new effects!

Inspired by tutorial on Designmodo.

Thanks Konstantine for Interstellar images!