需求前提

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. 红黑树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

    二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 近几天闲来无事...就把各种平衡树都写了一下... 下面是红黑树(Red Black Tree) 喜闻乐见拿到了luo ...

  2. 10、spark高级编程

    一.基于排序机制的wordcount程序 1.要求 1.对文本文件内的每个单词都统计出其出现的次数. 2.按照每个单词出现次数的数量,降序排序. 2.代码实现 ------java实现------- ...

  3. (29)打鸡儿教你Vue.js

    web阅读器开发 epub格式的解析原理 Vue.js+epub.js实现一个简单的阅读器 实现阅读器的基础功能 字号选择,背景颜色 有上一页,下一页的功能 设置字号,切换主题,进度按钮 电子书目录 ...

  4. UOJ226. 【UR #15】奥林匹克环城马拉松 [组合数学,图论]

    UOJ 思路 我们知道关于有向图欧拉回路计数有一个结论:在每个点入度等于出度的时候,答案就是 \[ t_w(G)\prod (deg_i-1)! \] 其中\(t_w(G)\)是以某个点为根的树形图个 ...

  5. MySQL5.7 基础之二

    设计范式: 第一范式:字段是原子性 第二范式:存在可用主键 第三范式:任何表都不应该有依赖于其它表非主键的字段 创建数据库.设计数据表 字段:字段名.数据类型.约束(通过键来实现,而键其实可以当做索引 ...

  6. Jmeter聚合报告理解

     Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,这里显示的就是 Name 属性的值 Samples:表示这次测试中一共发出了多少个请求 ...

  7. ls命令的简单实现

    ls命令的简单实现 目标:简单的实现ls命令 实现的mic_ls命令主要功能 1.循环遍历目录 2.列出目标目录所有的子目录和文件 3.列出文件的文件权限,所有者,文件大小等详细信息 参数 -r 循环 ...

  8. php手记之08-tp5中间件

    01-创建中间件 php think make:middleware 中间件的名称 这个指令会 application/http/middleware目录下面生成一个中间件文件. 02-注册中间件三种 ...

  9. Java-Maven(十):Maven 项目常用plugins

    本文主要总结最近一段时间使用maven时,遇到需要maven plugins的一些简单总结. 1)在Build下重新指定最终打包报名 <build> <!--最终打包的包名,如果这里 ...

  10. tengine无法解析ssi报错 Nginx: unsafe URI detected while sending response

    Nginx: unsafe URI detected while sending response 现象:# 类似 <!--#include virtual="../library/h ...