【iOS】ZFPlayer源码解读<上>

前言

最近看AVFoundation音视频类的东西,有了解到开源视频播放器ZFPlayer,在这里解读顺便了解学习一下作者的设计思路,如有不足或者错误,希望读者能够批评并指正。

这是作者对此框架的设计脑图。根据类名可以看出作者是按功能模块将之拆成了不同的类,这样结构比较清晰便于重用与组件化。

635942-5662bfec6d457cba.png
说明

用法作者在原文章有介绍用法原著,在这里只是解读研究源码与实现思路。

ZFPlayer 3.2.2 目前包含以下五个文件夹:


WX20181210-172911.png

在这里我也根据使用方法与功能模块,分为上中下3篇介绍,本篇为上篇,主要介绍 Core文件夹下相关类。使用pod 'ZFPlayer', '~> 3.0'安装即可。

Core文件夹中类与作用

安装成功后,所pod下来的文件是core文件夹,如下图:


Jietu20181210-105832.png

就像作者所说,这只是一个播放器的壳子,如果你想完全自定义 播放器AVPlayer控制视图AVPlayerControlView,这些文件就够了。但是,自定义的player需要严格遵循协议实现 ZFPlayerMediaPlayback协议并实现,自定义的controlView需要遵循ZFPlayerMediaControl协议,根据回调更新自定义的controlView。

既然player与controlView都自定义了,那还要此库有何用?此库帮我们做了什么?我们为什么还要继续使用此库?

  • ZFPlayerController: 主类,所有的事件,回调视频,播放器,都由此获取。便于统一管理协作。
// 正常视频播放
+ (instancetype)playerWithPlayerManager:(id<ZFPlayerMediaPlayback>)playerManager containerView:(UIView *)containerView;

// 列表视频播放
+ (instancetype)playerWithScrollView:(UIScrollView *)scrollView playerManager:(id<ZFPlayerMediaPlayback>)playerManager containerViewTag:(NSInteger)containerViewTag;
  • ZFOrientationObserver:封装了有关屏幕旋转,监听,布局更新。
    视频播放时,我们可根据协议使用来自定义自己的业务与相关布局。

  • ZFKVOController: 更加安全的使用KVO ( 与播放器直接业务逻辑无关 ),避免在直接使用时带来的一些因多次过度释放导致程序crash的副作用。建议参考一下facebook早期的的开源库FBKVOController

  • ZFPlayerGestureControl:多种手势管理的一个类,主要用于播放器的controlView,里面包含点击,双击,捏合,拖动等等,并优化了用户在使用播放器间这些手势间的之间的一些冲突。这些作者都已经帮我们完成并把手势识别的结果回调给我们,我们以此来操作控制层ControlView(UI)与播放器本身(逻辑)的业务如播放进度,播放器的暂停,播放,快进快退等等。

@property (nonatomic, copy, nullable) void(^singleTapped)(ZFPlayerGestureControl *control);
@property (nonatomic, copy, nullable) void(^doubleTapped)(ZFPlayerGestureControl *control);
@property (nonatomic, copy, nullable) void(^beganPan)(ZFPlayerGestureControl *control, ZFPanDirection direction, ZFPanLocation location);
@property (nonatomic, copy, nullable) void(^changedPan)(ZFPlayerGestureControl *control, ZFPanDirection direction, ZFPanLocation location, CGPoint velocity);
@property (nonatomic, copy, nullable) void(^endedPan)(ZFPlayerGestureControl *control, ZFPanDirection direction, ZFPanLocation location);

// 捏合
@property (nonatomic, copy, nullable) void(^pinched)(ZFPlayerGestureControl *control, float scale);

// 拖动方向,一开始是左右拖动的方向,或者是上下拖动的方向
@property (nonatomic, readonly) ZFPanDirection panDirection;

// 所处屏幕位置,如左边调节音量,右侧调整亮度
@property (nonatomic, readonly) ZFPanLocation panLocation;
@property (nonatomic, readonly) ZFPanMovingDirection panMovingDirection;

  • ZFPlayerLogManager:日志类,根据配置环境可实时查看控制台的一些相关的播放信息便于调试,这里不多说。

  • ZFPlayerNotification: 播放器系统的相关通知监听:包括前后,耳机插拔,音量等,如果自定义,根据此回调处理自身业务,当然如果这些不满足,可自行再添加回调,只是不方便使用pod了。

// 将要失去焦点
@property (nonatomic, copy, nullable) void(^willResignActive)(ZFPlayerNotification *registrar);

// 已经变更为活跃
@property (nonatomic, copy, nullable) void(^didBecomeActive)(ZFPlayerNotification *registrar);

// 插入耳机
@property (nonatomic, copy, nullable) void(^newDeviceAvailable)(ZFPlayerNotification *registrar);

// 拔出耳机
@property (nonatomic, copy, nullable) void(^oldDeviceUnavailable)(ZFPlayerNotification *registrar);

// AVAudioSession对象的category发生改变时
@property (nonatomic, copy, nullable) void(^categoryChange)(ZFPlayerNotification *registrar);

// 音量改变
@property (nonatomic, copy, nullable) void(^volumeChanged)(float volume);
  • ZFPlayerView : 基类,自己实现继承于此类,作者原码也是这样也实现的,后面会再写。

  • ZFReachabilityManager : 网络状态监听,同Reachability,AFNetworking...,因为库本身功能确实依赖网络状态,又避免使用者项目中可能会存在冲突,就为自己加了一个便一统一,也避免使用者麻烦。

  • ZFFloatView : 全局的小浮窗视图,可跟随手指的拖动而移动(UIPanGestureRecognizer),此类并无播放器相关业务。

  • UIViewController+ZFPlayerRotation: 方便控制当前的ViewController支持旋转及旋转的方向,里面重写的系统方法,有关屏幕旋转网上的解释也非常多,更多详情这里可以参考iOS 屏幕旋转。代码很固定,重写方法的字面意思也很好理解,用的时候根据自身业务与框架提供的布尔值即可,只是一个方向问题,难度不大,只是需要适配各种旋转情况。

注意:这几个方法只有当viewController是window的rootViewController或者viewController是present出来时,才会起作用!,所以如果想让各个viewController来控制自己的方向,重写UINavigationController,UITabbarController,UIViewController中固定的相关旋转的方法即可

  1. UITabbarController中重写以下方法
// 子控制器是否支持旋转
- (BOOL)shouldAutorotate {
    UIViewController *vc = self.viewControllers[self.selectedIndex];
    if ([vc isKindOfClass:[UINavigationController class]]) {
        UINavigationController *nav = (UINavigationController *)vc;
        return [nav.topViewController shouldAutorotate];
    } else {
        return [vc shouldAutorotate];
    }
}

// 子控制器支持旋转的所有方向,多选
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    UIViewController *vc = self.viewControllers[self.selectedIndex];
    if ([vc isKindOfClass:[UINavigationController class]]) {
        UINavigationController *nav = (UINavigationController *)vc;
        return [nav.topViewController supportedInterfaceOrientations];
    } else {
        return [vc supportedInterfaceOrientations];
    }
}

// 子控制器优先支持的方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    UIViewController *vc = self.viewControllers[self.selectedIndex];
    if ([vc isKindOfClass:[UINavigationController class]]) {
        UINavigationController *nav = (UINavigationController *)vc;
        return [nav.topViewController preferredInterfaceOrientationForPresentation];
    } else {
        return [vc preferredInterfaceOrientationForPresentation];
    }
}
  1. UINavigationController重写以下方法
// 同上
- (BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

// 同上
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return [self.topViewController supportedInterfaceOrientations];
}

// 同上
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.topViewController preferredInterfaceOrientationForPresentation];
}
  • UIScrollView+ZFPlayer : 滚动中在可视范围内播放,类似微博首页中的视频。这个类传入列表视图,里面会自动根据偏移计算位置并在设定的位置播放,此类里面大多是关于坐标的计算与列表的偏移更新,但是需要使用者自己主动调用以下以下代理。
- (void)zf_scrollViewDidEndDecelerating;
- (void)zf_scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate;
- (void)zf_scrollViewDidScrollToTop;
- (void)zf_scrollViewDidScroll;
- (void)zf_scrollViewWillBeginDragging;

以上这些类是被定义在Core文件夹下,然而并没有丝毫有关播放器与控制视图的任务业务逻辑。

最后

ZFPlayer最初版本并不是以协议为基准分散到各个类去实现,后来更新了整个功能结构采用协议进行自由定制与复用,每个类的所实现的功能点比较明确单一,这样也便于维护拓展,背后可以看出作者很用心并且此库更新的版本也比较快。
使用途中,最好组件复用,比如使用了ZFReachabilityManager,若项目中之前存在ReachabilityManager网络监听类可以保留其一,避免重复。



ZFPlayer源码解读<中>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343