• 捕捉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

    参考文档

  • 相关阅读:
    详测 Generics Collections TDictionary(2): ContainsKey、ContainsValue、AddOrSetValue、TryGetValue
    详测 Generics Collections TQueue (3): OnNotify、Extract
    详测 Generics Collections TDictionary(4): OnKeyNotify、OnValueNotify
    详测 Generics Collections TStack(1): Push、Pop、Peek 其他功能同 TQueue
    详测 Generics Collections TDictionary(1): Add、Items、Keys、Values、Remove
    测试字符串写入类: TStringWriter
    在IIS 6 中使用HTTP压缩(Compression)遇到的一个问题
    [好消息]博客园新服务器到货
    [公告]美化家园行动—博客园成立Skin设计小组
    [公告]星期天上午更换新服务器
  • 原文地址:https://www.cnblogs.com/widgetbox/p/11774074.html
Copyright © 2020-2023  润新知