在ComponentKit中播放视频

时间:2016-12-08 01:28:42

标签: ios facebook componentkit

我正在尝试使用ComponentKit显示视频。我想让它在点击组件时发挥作用。我找到了这个Github Issue,所以我知道我需要使用CKStatefulViewComponent,但我不知道处理tap事件的建议方法是使用ComponentKit。

这是我到目前为止的代码:

#import "CDBVideoPlayerComponent.h"
#import <ComponentKit/ComponentKit.h>

#import <ComponentKit/CKStatefulViewComponentController.h>
#import <AVFoundation/AVFoundation.h>

@interface CDBVideoPlayerComponent()
@property (nonatomic, strong) AVPlayer *player;
@end

@implementation CDBVideoPlayerComponent

+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {

    CKComponentScope scope(self, url);

    CDBVideoPlayerComponent *component = [super newWithSize:size accessibility:{}];

    component->_player = [[AVPlayer alloc] initWithURL:url];

    return component;
}

@end

@interface CDBVideoPlayerComponentController : CKStatefulViewComponentController
- (void)handleTapForPlayer:(AVPlayer *)player;
@end

@implementation CDBVideoPlayerComponentController

+ (UIView *)newStatefulView:(id)context {

    UIView *view = [[UIView alloc] init];
    view.backgroundColor = [UIColor darkGrayColor];

    AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
    playerLayer.frame = view.bounds;

    [view.layer addSublayer:playerLayer];

    return view;
}

+ (void)configureStatefulView:(UIView *)statefulView forComponent:(CDBVideoPlayerComponent *)videoComponent {

    __block AVPlayerLayer *layer = nil;

    for (CALayer *currentLayer in statefulView.layer.sublayers) {

        if ([[currentLayer class] isSubclassOfClass:[AVPlayerLayer class]]) {
            layer = (AVPlayerLayer*)currentLayer;
            break;
        }
    }

    if (layer) {
        layer.player = videoComponent.player;
    } else {
        layer.player = nil;
    }
}

- (void)handleTapForPlayer:(AVPlayer *)player {
    [player play];
}

@end

1 个答案:

答案 0 :(得分:1)

所以我找到了一个解决方案,它不是很干净,而且我不确定这是ComponentKit开发人员在开发方面的意图如何正确处理这种情况的方式应该看起来像,但这个解决方案有效:

首先,我们需要创建一个单独的视图来处理实际的视频演示。这是从Apple's example

中提取的
#import "CDBVideoPlayerView.h"

@implementation CDBVideoPlayerView

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

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

- (void)setPlayer:(AVPlayer *)player {
    [(AVPlayerLayer *)[self layer] setPlayer:player];
}

@end

然后,组件和控制器:

#import "CDBVideoPlayerComponent.h"

#import "CDBVideoPlayerView.h"

#import <ComponentKit/CKStatefulViewComponent.h>
#import <ComponentKit/CKStatefulViewComponentController.h>
#import <AVFoundation/AVFoundation.h>

@interface CDBVideoStateComponent : CKStatefulViewComponent
@property (nonatomic, strong) AVPlayer *player;

+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size;

@end

@implementation CDBVideoStateComponent

+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {

    CKComponentScope scope(self, url);

    CDBVideoStateComponent *component = [super newWithSize:size accessibility:{}];

    component->_player = [[AVPlayer alloc] initWithURL:url];
    component->_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;

    return component;
}

@end

@interface CDBVideoStateComponentController : CKStatefulViewComponentController
@end

@implementation CDBVideoStateComponentController

+ (UIView *)newStatefulView:(id)context {

    CDBVideoPlayerView *view = [[CDBVideoPlayerView alloc] init];
    return view;
}

+ (void)configureStatefulView:(CDBVideoPlayerView *)statefulView forComponent:(CDBVideoStateComponent *)videoComponent {

    statefulView.player = videoComponent.player;
}

@end

@interface CDBVideoPlayerComponent ()
@property (nonatomic, strong) AVPlayer *player;
@end

@implementation CDBVideoPlayerComponent

+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {

    CKComponentScope scope(self, url);

    CDBVideoStateComponent *component = [CDBVideoStateComponent newWithVideoURL:url size:size];

    CDBVideoPlayerComponent *playerComponent = [super newWithComponent:component
        overlay:
        [CKButtonComponent
            newWithTitles:{}
            titleColors:{}
            images:{}
            backgroundImages:{}
            titleFont:{}
            selected:NO
            enabled:YES
            action:@selector(handleButtonPress:)
            size:{}
            attributes:{}
            accessibilityConfiguration:{}
         ]
    ];

    playerComponent->_player = component.player;

    return playerComponent;
}

- (void)handleButtonPress:(id)sender {

    if (self.player.status == AVPlayerStatusReadyToPlay) {

        if (self.player.timeControlStatus == AVPlayerTimeControlStatusPaused || self.player.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
            [self.player play];
        } else {
            [self.player pause];
        }

    }
}

@end

修改

我还发现,通过将大部分代码移到VideoPlayerView,我认为是更清晰的解决方案

@implementation VideoPlayerView

- (instancetype)init {

    if (self = [super init]) {
        [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]];
    }

    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {

    if (self = [super initWithFrame:frame]) {
        [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]];
    }

    return self;
}

- (void)dealloc {
    [self removeObserversForPlayer:self.player];
}

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

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

- (void)setPlayer:(AVPlayer *)player {

    [self removeObserversForPlayer:self.player];

    [(AVPlayerLayer *)[self layer] setPlayer:player];

    [self addObserverForPlayer:player];
}

- (void)addObserverForPlayer:(AVPlayer *)player {
    if (player) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:player.currentItem];
    }
}

- (void)removeObserversForPlayer:(AVPlayer *)player {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:player.currentItem];
}

- (void)itemDidFinishPlaying:(NSNotification *)notification  {
    [self.player seekToTime:kCMTimeZero];
    [self.player pause];
}

- (void)handleTap:(id)sender {

    if (self.player.status == AVPlayerStatusReadyToPlay) {

        if (self.player.timeControlStatus == AVPlayerTimeControlStatusPaused || self.player.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
            [self.player play];
        } else {
            [self.player pause];
        }

    }
}

@end

更新的组件:

#import "VideoPlayerComponent.h"

#import "VideoPlayerView.h"

#import <ComponentKit/CKStatefulViewComponentController.h>
#import <AVFoundation/AVFoundation.h>
#import <ComponentKit/ComponentKit.h>

@interface VideoPlayerComponent ()
@property (nonatomic, strong) AVPlayer *player;

@end

@implementation VideoPlayerComponent

+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {

    CKComponentScope scope(self, url);

    VideoPlayerComponent *component = [super newWithSize:size accessibility:{}];

    component->_player = [[AVPlayer alloc] initWithURL:url];
    component->_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;

    return component;
}

@end

@interface VideoPlayerComponentController : CKStatefulViewComponentController
@end

@implementation VideoPlayerComponentController

+ (UIView *)newStatefulView:(id)context {

    VideoPlayerView *view = [[VideoPlayerView alloc] init];
    view.backgroundColor = [UIColor grayColor];
    return view;
}

+ (void)configureStatefulView:(VideoPlayerView *)statefulView forComponent:(VideoPlayerComponent *)videoComponent {

    statefulView.player = videoComponent.player;
}

@end