捕捉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日志,非常容易受到来自同事和面试官的嘲讽 ...
随机推荐
- SB的SDOISB记
恩, SDOI考完 考完. day0 : 上车时看myj费劲跨过行李的样子,嘲讽他腿短.很happy 吃饭时和myj吃了羊肉..汤 虽然贵, 但是意外的好吃 继续嘲讽myj不是男人,吃的少 不知怎么的 ...
- Unity 3d 实现物体跟随摄像机视野运动
https://blog.csdn.net/qq_31411825/article/details/61623857 Unity 3d 实现物体跟随摄像机视野运动Created by miccall ...
- C语言实现多线程排序
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h& ...
- 2018-2019 20165226 Exp9 Web安全基础
2018-2019 20165226 Exp9 Web安全基础 目录 一.实验内容说明及基础问题回答 二.实验过程 Webgoat准备 XSS攻击 ① Phishing with XSS 跨站脚本钓鱼 ...
- 类别不平衡问题之SMOTE算法(Python imblearn极简实现)
类别不平衡问题类别不平衡问题,顾名思义,即数据集中存在某一类样本,其数量远多于或远少于其他类样本,从而导致一些机器学习模型失效的问题.例如逻辑回归即不适合处理类别不平衡问题,例如逻辑回归在欺诈检测问题 ...
- iview3 版本 升级
Button 废弃 type ghost,原先的 default 样式有改变. Icon 的图标升级至 ionicons 3.0 图标,图标名称有改变. Breadcrumb 废弃 href 属性. ...
- .gitignore忽略多层文件夹用**
一.写法 **/bin/Debug/ 前面的两个*号代表任意多层上级文件夹 需要 git 1.8.2 及其以上的版本才支持 如何查看当前版本并且升级(windows) 二.如何升级 git是2.14. ...
- 项目启动时警告 Establishing SSL connection without server's identity verification is not recommended
项目启动时控制台提示警告: Tue May 14 23:16:10 CST 2019 WARN: Establishing SSL connection without server's identi ...
- http://man7.org/linux/man-pages/man2/epoll_wait.2.html
https://segmentfault.com/a/1190000007240744 https://baike.baidu.com/item/Glibc http://man7.org/linux ...
- ActiveMQ处理模式
一.PTP处理模式(Queue) 消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息. 消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消 ...