Animate AVPlayerLayer videoGravity property

2019-02-03 19:40发布

I'm trying to copy Apple's behavior in video playback that allows the user to stretch the video image to fill the bounds.

@interface FHVideoPlayerView : UIView
@end
@interface FHVideoPlayerView

+ (Class)layerClass
{
    return [AVPlayerLayer class];
}

- (void)setAspectMode:(FHVideoPlayerAspectMode)aspectMode animated:(BOOL)animated
{
    FHVideoPlayerAspectMode current = [self aspectMode];
    FHVideoPlayerAspectMode final   = aspectMode;

    NSString *fromValue;
    NSString *toValue;

    AVPlayerLayer *layer = (AVPlayerLayer *)[self layer];

    switch (current) {
        case FHVideoPlayerAspectFill:
            fromValue = AVLayerVideoGravityResizeAspectFill;
            break;
        case FHVideoPlayerAspectFit:
            fromValue = AVLayerVideoGravityResizeAspect;
            break;
       default:
           break;
    }

    switch (final) {
        case FHVideoPlayerAspectFill:
            toValue = AVLayerVideoGravityResizeAspectFill;
            break;
        case FHVideoPlayerAspectFit:
            toValue = AVLayerVideoGravityResizeAspect;
            break;
        default:
            break;
    }

    if (toValue != fromValue) {
        if (animated == YES) {
            // Manually added CABasicAnimation based on the understanding the implicit animations are disabled for CALayers that back a UIView
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"videoGravity"];
            [layer addAnimation:animation forKey:@"animateVideoGravity"];

            [CATransaction begin];
            [CATransaction setAnimationDuration:0.333];
        }

        [layer setVideoGravity:toValue];

        if (animated == YES) {
            [CATransaction commit];
        }
    }
}

This works just fine when I try and animate a numeric property such as opacity. But, you'll notice from the class reference, AVPlayerLayer's videoGravity property is an NSString. The class reference does points out that it is animatable.

So my question is: How!?

I believe this is probably related somehow to CALayer's content property and possibly contentsGravity.

2条回答
姐就是有狂的资本
2楼-- · 2019-02-03 19:59

It was a bug in iOS 5.0 and iOS 5.0.1.

After updating to iOS 5.1 the video gravity changing animation begun to work again.

查看更多
甜甜的少女心
3楼-- · 2019-02-03 20:15

As voromax pointed out, this is a bug in iOS 5.0. I reverse engineered -[AVPlayerLayer setVideoGravity:] implementation in iOS 5.1 in order to understand how the animation was supposed to work. Here is how to workaround the bug and have a nice animation on iOS 5.0.

@implementation VideoPlayerView

+ (Class) layerClass
{
    return [AVPlayerLayer class];
}

- (AVPlayerLayer *) playerLayer
{
    return (AVPlayerLayer *)[self layer];
}

- (NSString *) videoGravity
{
    return self.playerLayer.videoGravity;
}

- (void) setVideoGravity:(NSString *)videoGravity
{
    self.playerLayer.videoGravity = videoGravity;

    // Workaround a bug in iOS 5.0
    float avFoundationVersion = [[[NSBundle bundleForClass:[AVPlayerLayer class]] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] floatValue];
    if (avFoundationVersion < 292.24f)
    {
        @try
        {
            NSString *contentLayerKeyPath = [NSString stringWithFormat:@"%1$@%2$@.%3$@%2$@", @"player", [@"layer" capitalizedString], @"content"]; // playerLayer.contentLayer
            CALayer *contentLayer = [self.playerLayer valueForKeyPath:contentLayerKeyPath];
            if ([contentLayer isKindOfClass:[CALayer class]])
                [contentLayer addAnimation:[CABasicAnimation animation] forKey:@"sublayerTransform"];
        }
        @catch (NSException *exception)
        {
        }
        self.bounds = self.bounds;
    }
}

@end
查看更多
登录 后发表回答