捕捉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日志,非常容易受到来自同事和面试官的嘲讽 ...
随机推荐
- lixuxmint系统定制与配置(1)-系统初始配置
小书匠Linux 经常安装新的系统,每次安装完都得去搜索一边如何将系统部署为之前的环境,不仅耗费时间,还不一定能弄回之前的环境,现在把从裸机->到工作环境的系统定制及配置过程记录下来,期间的配置 ...
- 【概率论】5-6:正态分布(The Normal Distributions Part III)
title: [概率论]5-6:正态分布(The Normal Distributions Part III) categories: - Mathematic - Probability keywo ...
- 项目发布到tomcat后,通过项目名称访问报404
查看tomcat发布地址发现和项目名称不一致 如果直接拷贝项目,可能也需要修改此选项 解决方案: 经过排查发现了原因: 首先说明一下项目 Maven+SSM 需要使用到 maven tomcat 7 ...
- 【Python 代码】生成hdf5文件
import random from PIL import Image import numpy as np import os import h5py from PIL import Image L ...
- (二)SQL学习之数据定义类SQL
以mysql为例 对数据库的常用操作 创建数据库:CREATE DATABASE mydb; 删除数据库:DROP DATABASE mydb; 切换数据库:USE mydb; 查询数据库:SHOW ...
- linux 9.故障修复和网络配置
一.linux系统故障修复 1.不知道root密码的前提下 破解root密码 服务器必须在身边 grub引导菜单 按任意键进入->e ( ...
- C语言JSON序列化/反序列化
最近想找一个C语言处理嵌套结构体和结构体数组的json库,理想的是能够很容易处理复杂结构体嵌套,并且使用简单的,但是没找到比较合适的,于是打算自己封装一个: 两个问题: C语言结构体本身没有元数据,这 ...
- [Windows] 输入字符间距变宽
今天在输入时,不会到误触到哪里,输入的字符间距变得很宽,如下图: 最后找到原因是不小心同时按下了 Shift+Space(空格),进入全角模式,就会导致输入的字符间距变宽 想要恢复,再按一次 shif ...
- ubuntu18.04 qemu环境搭建【学习笔记】
一.准备工具 1.1 安装相关工具 sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential 1. ...
- Spring’s RestTemplate
Spring’s RestTemplate /** * After the word document is generated in memory we can upload it to the s ...