捕捉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 == 1) {
            //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 == 0 || !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 == 0) {
        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 == 0 || !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 == 0) {
        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

参考文档

原文地址:https://www.cnblogs.com/widgetbox/p/11774074.html