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

 

參考文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM