需求前提

1. app内轻量级的视频播放功能,故不希望引入“过度开发、过度封装”的第三方控件组,使用原生的AVPlayerViewController

2. 工具栏有新增控件需求,如下载按钮 等

3. 希望自定义的控件组可以伴随 系统原生控件组一起出现或隐藏

需要的第三方库

aop框架组

pod 'Aspects'

实施步骤

1.新增两个属性,记录要操作的view和HOOK的对象信息

//记录音量控制的父控件,控制它隐藏显示的 view
@property (nonatomic, weak)UIView *volumeSuperView;
//记录我们 hook 的对象信息
@property (nonatomic, strong)id<AspectToken>hookAVPlaySingleTap;

2.在视频播放器响应手势事件时进行HOOK

Class UIGestureRecognizerTarget = NSClassFromString(@"UIGestureRecognizerTarget");
_hookAVPlaySingleTap = [UIGestureRecognizerTarget aspect_hookSelector:@selector(_sendActionWithGestureRecognizer:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo>info,UIGestureRecognizer *gest){
if (gest.numberOfTouches == ) {
//AVVolumeButtonControl
if (!self.volumeSuperView) {
UIView *view = [gest.view findViewByClassName:@"AVVolumeButtonControl"];
if (view) {
while (view.superview) {
view = view.superview;
if ([view isKindOfClass:[NSClassFromString(@"AVTouchIgnoringView") class]]) {
self.volumeSuperView = view;
[view HF_addObserverForKeyPath:@"hidden" block:^(__weak id object, id oldValue, id newValue) {
NSLog(@"newValue ==%@",newValue);
BOOL isHidden = [(NSNumber *)newValue boolValue];
dispatch_async(dispatch_get_main_queue(), ^{
//做同步显隐操作 }); }];
break;
}
}
}
}
} } error:nil];

3. 出控制器时,应移除HOOK

- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.hookAVPlaySingleTap remove];
}

部分category工具方法代码

- (UIView *)findViewByClassName:(NSString *)className
{
UIView *view;
if ([NSStringFromClass(self.class) isEqualToString:className]) {
return self;
} else {
for (UIView *child in self.subviews) {
view = [child findViewByClassName:className];
if (view != nil) break;
}
}
return view;
}
#import "NSObject+BlockObserver.h"
#import <objc/message.h> @interface HFDefaultObserver : NSObject
@property (nonatomic, copy) HFKVOblock kvoBlock;
@property (nonatomic, copy) HFNotificationBlock notificationBlock; @end @implementation HFDefaultObserver
- (instancetype)initWithKVOBlock:(HFKVOblock)kvoBlock
{
if (self = [super init]) {
_kvoBlock = kvoBlock;
}
return self;
} - (instancetype)initWithNotificationBlock:(HFNotificationBlock)notificationBlock
{
if (self = [super init]) {
_notificationBlock = notificationBlock;
}
return self;
} //实现监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if (!self.kvoBlock) {
return;
}
BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
if (isPrior) {
return;
} NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
if (changeKind != NSKeyValueChangeSetting) {
return;
}
id oldValue = [change objectForKey:NSKeyValueChangeOldKey];
if (oldValue == [NSNull null]) {
oldValue = nil;
}
id newValue = [change objectForKey:NSKeyValueChangeNewKey];
if (newValue == [NSNull null]) {
newValue = nil;
}
if (oldValue != newValue) {
self.kvoBlock(object, oldValue, newValue);
}
} - (void)handleNotification:(NSNotification *)notification
{
!self.notificationBlock ?: self.notificationBlock(notification);
} @end @implementation NSObject (BlockObserver) static NSString * const KHFObserverKey = @"KHFObserverKey";
static NSString * const KHFNotificationObserversKey = @"KHFNotificationObserversKey"; // 替换dealloc方法,自动注销observer
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalDealloc = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
Method newDealloc = class_getInstanceMethod(self, @selector(autoRemoveObserverDealloc));
method_exchangeImplementations(originalDealloc, newDealloc);
});
} - (void)autoRemoveObserverDealloc
{
if (objc_getAssociatedObject(self, (__bridge const void *)KHFObserverKey) || objc_getAssociatedObject(self, (__bridge const void *)KHFNotificationObserversKey)) {
[self HF_removeAllObserverBlocks];
[self HF_removeAllNotificationBlocks];
}
//这句相当于直接调用dealloc
[self autoRemoveObserverDealloc];
} - (void)HF_addObserverForKeyPath:(NSString *)keyPath block:(HFKVOblock)block
{
if (keyPath.length == || !block) {
return;
}
NSMutableDictionary *observersDict = objc_getAssociatedObject(self, (__bridge const void *)KHFObserverKey);
if (!observersDict) {
observersDict = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, (__bridge const void *)KHFObserverKey, observersDict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
NSMutableArray * observers = [observersDict objectForKey:keyPath];
if (!observers) {
observers = [NSMutableArray array];
[observersDict setObject:observers forKey:keyPath];
}
HFDefaultObserver *observer = [[HFDefaultObserver alloc] initWithKVOBlock:block];
[self addObserver:observer forKeyPath:keyPath options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
[observers addObject:observer];
} - (void)HF_removeObserverBlocksForKeyPath:(NSString *)keyPath
{
if (keyPath.length == ) {
return;
}
NSMutableDictionary *observersDict = objc_getAssociatedObject(self, (__bridge const void *)KHFObserverKey);
if (observersDict) {
NSMutableArray *observers = [observersDict objectForKey:keyPath];
[observers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[self removeObserver:obj forKeyPath:keyPath];
}];
[observersDict removeObjectForKey:keyPath];
}
} - (void)HF_removeAllObserverBlocks
{
NSMutableDictionary *observersDict = objc_getAssociatedObject(self, (__bridge const void *)KHFObserverKey);
if (observersDict) { [observersDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSMutableArray *obsevers, BOOL * _Nonnull stop) {
[obsevers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[self removeObserver:obj forKeyPath:key];
}];
}];
[observersDict removeAllObjects];
}
} - (void)HF_addNotificationForName:(NSString *)name block:(HFNotificationBlock)block
{
if (name.length == || !block) {
return;
}
NSMutableDictionary *observersDict = objc_getAssociatedObject(self, (__bridge const void *)KHFNotificationObserversKey);
if (!observersDict) {
observersDict = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, (__bridge const void *)KHFNotificationObserversKey, observersDict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
NSMutableArray *observers = [observersDict objectForKey:name];
if (!observers) {
observers = [NSMutableArray array];
[observersDict setObject:observers forKey:name];
}
HFDefaultObserver *observer = [[HFDefaultObserver alloc] initWithNotificationBlock:block];
[[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(handleNotification:) name:name object:nil];
[observers addObject:observer]; } - (void)HF_removeNotificationBlocksForName:(NSString *)name
{
if (name.length == ) {
return;
}
NSMutableDictionary *observersDict = objc_getAssociatedObject(self, (__bridge const void *)KHFNotificationObserversKey);
if (observersDict) {
NSMutableArray *observers = [observersDict objectForKey:name];
[observers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[[NSNotificationCenter defaultCenter] removeObserver:obj name:name object:nil];
}];
[observersDict removeObjectForKey:name];
} } - (void)HF_removeAllNotificationBlocks
{
NSMutableDictionary *observersDict = objc_getAssociatedObject(self, (__bridge const void *)KHFNotificationObserversKey);
[observersDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSMutableArray *observers, BOOL * _Nonnull stop) {
[observers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[[NSNotificationCenter defaultCenter] removeObserver:obj name:key object:nil];
}];
}];
[observersDict removeAllObjects];
} @end
#import <Foundation/Foundation.h>

typedef void(^HFKVOblock)(__weak id object, id oldValue, id newValue);
typedef void(^HFNotificationBlock)(NSNotification *notification);
@interface NSObject (BlockObserver) - (void)HF_addObserverForKeyPath:(NSString *)keyPath block:(HFKVOblock)block;
- (void)HF_removeObserverBlocksForKeyPath:(NSString *)keyPath;
- (void)HF_removeAllObserverBlocks; - (void)HF_addNotificationForName:(NSString *)name block:(HFNotificationBlock)block;
- (void)HF_removeNotificationBlocksForName:(NSString *)name;
- (void)HF_removeAllNotificationBlocks;

@end

参考文档

捕捉AVPlayerViewController 系统原生工具栏的出现、隐藏事件的更多相关文章

  1. position导致Safari工具栏不自动隐藏

    一般情况下,移动端网页在上滑的时候,Safari的工具栏会自动隐藏掉,下滑的时候又会出现. 但是,如果可滑动区域的最外层box写了position:absolute,就不会自动隐藏了. 例如像这样的页 ...

  2. iOS系统原生 二维码的生成、扫描和读取(高清、彩色)

    由于近期工作中遇到了个需求:需要将一些固定的字段 在多个移动端进行相互传输,所以就想到了 二维码 这个神奇的东东! 现在的大街上.连个摊煎饼的大妈 都有自己的二维码来让大家进行扫码支付.可见现在的二维 ...

  3. 利用 Android 系统原生 API 实现分享功能

    利用 Android 系统原生 API 实现分享功能 这篇文章提供一个封装好的 Share2 库供大家参考. GitHub 项目地址:Share2 大家知道,要调用 Android 系统内建的分享功能 ...

  4. UIActivityViewController实现系统原生分享

    代码地址如下:http://www.demodashi.com/demo/11042.html 一.效果预览 二.接下来介绍UIActivityViewController,跟我动手做 1.创建要分享 ...

  5. 昨天所写的JQ 点击隐藏事件,关键性原理

    JQ 点击隐藏事件,关键性原理 1.JQ 库的调用 一般选择为: 1)库越小越好 2)库的功能越强大越好 <script src="js/jquery.js" type=&q ...

  6. IOS系统中使用zepto的live事件绑定不了的一个办法

    IOS系统中使用zepto的live事件绑定不了的一个办法: 对事件对象添加样式:cursor:pointer

  7. 原生JS元素怎么取消事件

    关于原生JS元素怎么取消事件,有3种方式 常规方法:removeEventListener 案例: <body> <div id="myDIV"> div ...

  8. React—Native开发之原生模块向JavaScript发送事件

    首先,由RN中文网关于原生模块(Android)的介绍可以看到,RN前端与原生模块之 间通信,主要有三种方法: (1)使用回调函数Callback,它提供了一个函数来把返回值传回给JavaScript ...

  9. tail -fn 1000 test.log | grep '关键字' 按照时间段 sed -n '/2014-12-17 16:17:20/,/2014-12-17 16:17:36/p' test.log /var/log/wtmp 该日志文件永久记录每个用户登录、注销及系统的启动、停机的事件

    Linux 6种日志查看方法,不会看日志会被鄙视的 2020-02-11阅读 7.3K0   作为一名后端程序员,和Linux打交道的地方很多,不会看Linux日志,非常容易受到来自同事和面试官的嘲讽 ...

随机推荐

  1. Codevs 2185【模板】最长公共上升子序列

    题目描述 Description 熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目.小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们要研究最长公共上升子序列了.小沐沐说,对于 ...

  2. Day13:H5+JS+C3

    css布局中,什么是BFC BFC是Block formatting context的缩写,表示"块级格式化上下文". 设置BFC的元素,是一个独立的渲染区域,只有Block-le ...

  3. make和rpm的编译、打包总结

    1  make工具使用 1.1 makefile基本规则 Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作. Makefile的规则: tar ...

  4. Redis面试问答(子文章)(持续更新)

    -----> 总文章 入口 文章目录 [-----> 总文章 入口](https://blog.csdn.net/qq_37214567/article/details/90174445) ...

  5. windows--zabbix-agent添加主机

        1.首先在 C 盘根目录下创建 zabbix 的文件夹 2.将需要的文件拖到该文件夹内(bin/win64) 3.修改 windows 配置文件(zabbix.agent.win.conf)的 ...

  6. 请在mysql配置文件修sql-mode或sql_mode为NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

    错误信息:请在mysql配置文件修sql-mode或sql_mode为NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 解决办法(最有效,以MySQL5.7为例): ...

  7. pyinstaller在64位系统下打包32位程序

    使用环境说明:win10 64位,已安装python3.6-64位版本 遇到的问题:win10 64位打包成exe文件后,不能在32位系统运行 需求:使用python打包生成exe文件,win64位和 ...

  8. svn更新,清理,上传时出现乱码解决方案

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_35703883/articl ...

  9. Cesium - Fabric 材质【转官网】

    https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric Fabric Hannah edited this page on 24 Dec ...

  10. CMU Database Systems - Concurrency Control Theory

    并发控制是数据库理论里面最难的课题之一 并发控制首先了解一下事务,transaction 定义如下, 其实transaction关键是,要满足ACID属性, 左边的正式的定义,由于的intuitive ...