Creating a Follicular Focus Effect on a UICollectionViewCell

How do you create a parallax focusing effect on a collection view cell with a custom view? If I used the image view, the property to set would be adjustsImageWhenAncestorFocused , but the collection view cell contained a subclass of UIView with custom content drawn using the main graphics.

+6
source share
3 answers

The answer from @raulriera is good, but just shifts the cell around in 2D. In addition, the OP requested an example of objective-C.

I also wanted to make this effect for the same reason - I had a UICollectionView with cells containing images and labels.

I created a subclass of UIMotionEffectGroup, since approaching the Apple TV effect requires four different motion effects. The first two are planar motions, as in @raulriera, and the other two are 3D rotations.

Just a shiny layer of medium to go now. Any takers ?:-)

Here is my code for the motion effects group:

(The shiftDistance and tiltAngle constants determine the effect size. These values ​​are very similar to the Apple TV effect.)

 #import <UIKit/UIKit.h> #import "UIAppleTvMotionEffectGroup.h" @implementation UIAppleTvMotionEffectGroup - (id)init { if ((self = [super init]) != nil) { // Size of shift movements CGFloat const shiftDistance = 10.0f; // Make horizontal movements shift the centre left and right UIInterpolatingMotionEffect *xShift = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; xShift.minimumRelativeValue = [NSNumber numberWithFloat: shiftDistance * -1.0f]; xShift.maximumRelativeValue = [NSNumber numberWithFloat: shiftDistance]; // Make vertical movements shift the centre up and down UIInterpolatingMotionEffect *yShift = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; yShift.minimumRelativeValue = [NSNumber numberWithFloat: shiftDistance * -1.0f]; yShift.maximumRelativeValue = [NSNumber numberWithFloat: shiftDistance]; // Size of tilt movements CGFloat const tiltAngle = M_PI_4 * 0.125; // Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation. UIInterpolatingMotionEffect *xTilt = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; // CATransform3D value for minimumRelativeValue CATransform3D transMinimumTiltAboutY = CATransform3DIdentity; transMinimumTiltAboutY.m34 = 1.0 / 500; transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0); // CATransform3D value for maximumRelativeValue CATransform3D transMaximumTiltAboutY = CATransform3DIdentity; transMaximumTiltAboutY.m34 = 1.0 / 500; transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0); // Set the transform property boundaries for the interpolation xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutY]; xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutY]; // Now make vertical movements effect a rotation about the X axis for up and down rotation. UIInterpolatingMotionEffect *yTilt = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; // CATransform3D value for minimumRelativeValue CATransform3D transMinimumTiltAboutX = CATransform3DIdentity; transMinimumTiltAboutX.m34 = 1.0 / 500; transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0); // CATransform3D value for maximumRelativeValue CATransform3D transMaximumTiltAboutX = CATransform3DIdentity; transMaximumTiltAboutX.m34 = 1.0 / 500; transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0); // Set the transform property boundaries for the interpolation yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutX]; yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutX]; // Add all of the motion effects to this group self.motionEffects = @[xShift, yShift, xTilt, yTilt]; [xShift release]; [yShift release]; [xTilt release]; [yTilt release]; } return self; } @end 

I used it like this in my subclass of UICollectionViewCell:

 @implementation MyCollectionViewCell - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator { // Create a static instance of the motion effect group (could do this anywhere, really, maybe init would be better - we only need one of them.) static UIAppleTVMotionEffectGroup *s_atvMotionEffect = nil; if (s_atvMotionEffect == nil) { s_atvMotionEffect = [[UIAppleTVMotionEffectGroup alloc] init]; } [coordinator addCoordinatedAnimations: ^{ if (self.focused) { [self addMotionEffect: s_atvMotionEffect]; } else { [self removeMotionEffect: s_atvMotionEffect]; } completion: ^{ }]; } @end 
+7
source

All you have to do is add UIMotionEffect to your subtitles. Something like that

 override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { coordinator.addCoordinatedAnimations({ [unowned self] in if self.focused { let verticalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y", type: .TiltAlongVerticalAxis) verticalMotionEffect.minimumRelativeValue = -10 verticalMotionEffect.maximumRelativeValue = 10 let horizontalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x", type: .TiltAlongHorizontalAxis) horizontalMotionEffect.minimumRelativeValue = -10 horizontalMotionEffect.maximumRelativeValue = 10 let motionEffectGroup = UIMotionEffectGroup() motionEffectGroup.motionEffects = [horizontalMotionEffect, verticalMotionEffect] yourView.addMotionEffect(motionEffectGroup) } else { // Remove the effect here } }, completion: nil) } 
+5
source

I converted Simon Tilson's answer to swift 3.0 and posted here to save typing for people in the future. Thanks so much for a great solution.

 class UIAppleTVMotionEffectGroup : UIMotionEffectGroup{ // size of shift movements let shiftDistance : CGFloat = 10.0 let tiltAngle : CGFloat = CGFloat(M_PI_4) * 0.125 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init() { super.init() // Make horizontal movements shift the centre left and right let xShift = UIInterpolatingMotionEffect(keyPath: "center.x", type: UIInterpolatingMotionEffectType.tiltAlongHorizontalAxis) xShift.minimumRelativeValue = shiftDistance * -1.0 xShift.maximumRelativeValue = shiftDistance let yShift = UIInterpolatingMotionEffect(keyPath: "center.y", type: UIInterpolatingMotionEffectType.tiltAlongVerticalAxis) yShift.minimumRelativeValue = 0.0-shiftDistance yShift.maximumRelativeValue = shiftDistance let xTilt = UIInterpolatingMotionEffect(keyPath: "layer.transform", type: UIInterpolatingMotionEffectType.tiltAlongHorizontalAxis) var transMinimumTiltAboutY = CATransform3DIdentity transMinimumTiltAboutY.m34 = 1.0 / 500.0 transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0) var transMaximumTiltAboutY = CATransform3DIdentity transMaximumTiltAboutY.m34 = 1.0 / 500.0 transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle , 0, 1, 0) xTilt.minimumRelativeValue = transMinimumTiltAboutY xTilt.maximumRelativeValue = transMaximumTiltAboutY let yTilt = UIInterpolatingMotionEffect(keyPath: "layer.transform", type: UIInterpolatingMotionEffectType.tiltAlongVerticalAxis) var transMinimumTiltAboutX = CATransform3DIdentity transMinimumTiltAboutX.m34 = 1.0 / 500.0 transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0) var transMaximumTiltAboutX = CATransform3DIdentity transMaximumTiltAboutX.m34 = 1.0 / 500.0 transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle , 1, 0, 0) yTilt.minimumRelativeValue = transMinimumTiltAboutX yTilt.maximumRelativeValue = transMaximumTiltAboutX self.motionEffects = [xShift,yShift,xTilt,yTilt] } 

}

I added a little pop to the subclass part of UICollectionView. Note the structural wrapper for the static variable

  override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) { struct wrapper { static let s_atvMotionEffect = UIAppleTVMotionEffectGroup() } coordinator.addCoordinatedAnimations( { var scale : CGFloat = 0.0 if self.isFocused { self.addMotionEffect(wrapper.s_atvMotionEffect) scale = 1.2 } else { self.removeMotionEffect(wrapper.s_atvMotionEffect) scale = 1.0 } let transform = CGAffineTransform(scaleX: scale, y: scale) self.layer.setAffineTransform(transform) },completion: nil) } 
+3
source

All Articles