在UICollectionViewCell上创建视差焦点效果

时间:2015-12-01 19:52:19

标签: objective-c uicollectionview tvos

如何使用自定义视图在集合视图单元格上创建视差焦点效果?如果我使用图像视图,则要设置的属性为adjustsImageWhenAncestorFocused,但我的集合视图单元格包含一个子类UIView,其中包含使用核心图形绘制的自定义内容。

3 个答案:

答案 0 :(得分:7)

@raulriera的答案很好,但只是在2D中移动单元格。 此外,OP要求一个客观C示例。

我也希望以同样的理由来做这个效果 - 我的UICollectionView包含包含图像和标签的单元格。

我创建了一个UIMotionEffectGroup子类,因为接近Apple TV效果似乎需要四种不同的动作效果。前两个是@raulriera中的平面运动,另外两个是3D旋转。

现在只需要闪亮的环境层。任何接受者? : - )

以下是我的动作效果组代码:

(shiftDistance和tiltAngle常量设置效果的大小。给定的值看起来与Apple TV效果非常相似。)

#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

我在自定义的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

答案 1 :(得分:5)

您需要做的就是为子视图添加UIMotionEffect。像这样的东西

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)
}

答案 2 :(得分:4)

我已将Simon Tillson的答案转换为swift 3.0并在此发布,以便为将来的人们节省打字。非常感谢您提供了一个很好的解决方案。

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]

}

}

我在UICollectionView子类的部件中添加了一个pop。请注意静态变量

的struct wrapper
  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)


}