捕捉AVPlayerViewController 系统原生工具栏的出现、隐藏事件
需求前提
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 系统原生工具栏的出现、隐藏事件的更多相关文章
- position导致Safari工具栏不自动隐藏
一般情况下,移动端网页在上滑的时候,Safari的工具栏会自动隐藏掉,下滑的时候又会出现. 但是,如果可滑动区域的最外层box写了position:absolute,就不会自动隐藏了. 例如像这样的页 ...
- iOS系统原生 二维码的生成、扫描和读取(高清、彩色)
由于近期工作中遇到了个需求:需要将一些固定的字段 在多个移动端进行相互传输,所以就想到了 二维码 这个神奇的东东! 现在的大街上.连个摊煎饼的大妈 都有自己的二维码来让大家进行扫码支付.可见现在的二维 ...
- 利用 Android 系统原生 API 实现分享功能
利用 Android 系统原生 API 实现分享功能 这篇文章提供一个封装好的 Share2 库供大家参考. GitHub 项目地址:Share2 大家知道,要调用 Android 系统内建的分享功能 ...
- UIActivityViewController实现系统原生分享
代码地址如下:http://www.demodashi.com/demo/11042.html 一.效果预览 二.接下来介绍UIActivityViewController,跟我动手做 1.创建要分享 ...
- 昨天所写的JQ 点击隐藏事件,关键性原理
JQ 点击隐藏事件,关键性原理 1.JQ 库的调用 一般选择为: 1)库越小越好 2)库的功能越强大越好 <script src="js/jquery.js" type=&q ...
- IOS系统中使用zepto的live事件绑定不了的一个办法
IOS系统中使用zepto的live事件绑定不了的一个办法: 对事件对象添加样式:cursor:pointer
- 原生JS元素怎么取消事件
关于原生JS元素怎么取消事件,有3种方式 常规方法:removeEventListener 案例: <body> <div id="myDIV"> div ...
- React—Native开发之原生模块向JavaScript发送事件
首先,由RN中文网关于原生模块(Android)的介绍可以看到,RN前端与原生模块之 间通信,主要有三种方法: (1)使用回调函数Callback,它提供了一个函数来把返回值传回给JavaScript ...
- 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日志,非常容易受到来自同事和面试官的嘲讽 ...
随机推荐
- Java 日期工具类(日期,月份加减等)--转
package util; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.C ...
- flutter 踩坑小计: amap_base 地图缩放 zoom 设置无效的问题
这种问题估计也就我这种菜鸡能遇到了,因为我问了一些大佬,他们完全没遇到这类的问题. 如果你也遇到了,希望这篇文章能帮到你,倘若还不能解决你的问题,可以留言,我们共同研究. 问题:同样的插件,为什么偏偏 ...
- 数据结构Java版之红黑树(八)
红黑树是一种自动平衡的二叉查找树,因为存在红黑规则,所以有效的防止了二叉树退化成了链表,且查找和删除的速度都很快,时间复杂度为log(n). 什么是红黑规则? 1.根节点必须是黑色的. 2.节点颜色要 ...
- Windowns下code: command not found
错误信息:code: command not found’ 解决办法:配置VsCode环境变量 首先找到VsCode点击其属性,查看路径 进入这个路径后 将D:\Program Files\Micro ...
- Spring 源码学习之环境搭建
一.下载Spring 源码 进入 https://github.com/spring-projects/spring-framework/tags 选择下载spring freamework的版本 h ...
- (转)AutoML 与轻量模型大列表: awesome-AutoML-and-Lightweight-Models
Awesome-AutoML-and-Lightweight-Models 原文:http://bbs.cvmart.net/articles/414/zi-yuan-automl-yu-qing-l ...
- iTop汉化
- Python classes to extract information from the Linux kernel /proc files.
python/python-linux-procfs/python-linux-procfs.git - Python classes to extract information from the ...
- 关于微信手机端IOS系统中input输入框无法输入的问题
如果网站不需要阻止用户的选择内容的行为就可以使用如下样式: * { -webkit-user-select: text; -user-select: text;}另一种方式: *: not(input ...
- ztree checkbox父子联动
1. 对于ztree而言,如果需要设置或者取消ztree的父子联动,只要在setting里面设置chkboxType的参数即可: 其中Y表示被checkbox被勾选时的联动情况,N表示取消勾选时的联动 ...