需求前提app
1. app內輕量級的視頻播放功能,故不但願引入「過分開發、過分封裝」的第三方控件組,使用原生的AVPlayerViewController框架
2. 工具欄有新增控件需求,以下載按鈕 等async
3. 但願自定義的控件組能夠伴隨 系統原生控件組一塊兒出現或隱藏工具
須要的第三方庫atom
aop框架組spa
pod 'Aspects'
實施步驟code
1.新增兩個屬性,記錄要操做的view和HOOK的對象信息視頻
//記錄音量控制的父控件,控制它隱藏顯示的 view @property (nonatomic, weak)UIView *volumeSuperView; //記錄咱們 hook 的對象信息 @property (nonatomic, strong)id<AspectToken>hookAVPlaySingleTap;
2.在視頻播放器響應手勢事件時進行HOOKserver
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