class_addMethod
應用場景
如上圖,右邊彈出的一個毛玻璃視圖,點擊非玻璃區,毛玻璃頁面從右邊消失。這個是分享頁面,其他的還有好多頁面,比如設置,列表。所有右邊彈出的view大小都一樣。
可以寫一個公用的背景,在那個背景上做進入和退出的動作。這裏就要用到 runtime
的 class_addMethod
了。
直接上代碼:
NS_ASSUME_NONNULL_BEGIN
@protocol LiveBroadsideViewProtocol <NSObject>
///從右邊彈出的view 有默認的實現
@optional
- (void)moveIn;
- (void)moveOut:(dispatch_block_t)completeBlock;
@end
///全屏側邊彈出view
@interface PTVLiveBroadsideView : UIView
///顯示View
- (void)showView:(id<LiveBroadsideViewProtocol> )view;
@end
NS_ASSUME_NONNULL_END
實現部分
#import "PTVLiveBroadsideView.h"
///通用頭文件
#import "PTVMacro.h"
///runtime
#import <objc/runtime.h>
///從右邊彈出的view添加默認的實現
void moveIn(id self, SEL _cmd) {
if (![self isKindOfClass:[UIView class]]) {
return;
}
UIView *view = self;
CGRect frame = CGRectMake(SCREEN_WIDTH, 0, 350, SCREEN_HEIGHT);
view.frame = frame;
[UIView animateWithDuration:0.25
animations:^{
CGRect frame1 = CGRectMake(SCREEN_WIDTH - 350, 0, 350, SCREEN_HEIGHT);
view.frame = frame1;
} completion:^(BOOL finished) {
}];
}
///默認的MoveOut 實現
void moveOut(UIView *self, SEL _cmd, dispatch_block_t completeBlock) {
[UIView animateWithDuration:0.25
animations:^{
CGRect frame1 = CGRectMake(SCREEN_WIDTH, 0, 350, SCREEN_HEIGHT);
self.frame = frame1;
} completion:^(BOOL finished) {
[self removeFromSuperview];
if (completeBlock) {
completeBlock();
}
}];
}
@interface PTVLiveBroadsideView()
///當前顯示的子View
@property (nonatomic, weak) UIView *currentView;
@end
@implementation PTVLiveBroadsideView
- (void)showView:(id<LiveBroadsideViewProtocol>)view {
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[(id<LiveBroadsideViewProtocol>)obj moveOut:^{
}];
}];
UIView *_v = (UIView *)view;
if (![_v isKindOfClass:[UIView class]]) return;
if (![view respondsToSelector:@selector(moveIn)]) {
BOOL sucess = class_addMethod([view class], @selector(moveIn), (IMP)moveIn, "i@:@");
if (!sucess) {
return;
}
}
if (_v.superview != self) {
[self addSubview:_v];
}
[view moveIn];
_currentView = _v;
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
UITouch *touch = touches.anyObject;
CGPoint point = [touch locationInView:self];
if (CGRectContainsPoint(_currentView.frame, point)) return;
if (![_currentView respondsToSelector:@selector(moveOut:)]) {
BOOL sucess = class_addMethod([_currentView class], @selector(moveOut:), (IMP)moveOut, "i@:@");
if (!sucess) {
[_currentView removeFromSuperview];
[self removeFromSuperview];
return;
}
}
WeakSelf;
[(id<LiveBroadsideViewProtocol>)_currentView moveOut:^{
[weakSelf removeFromSuperview];
}];
}
@end
動態添加方法思路是看看新加入的view 有沒有實現 moveIn 或moveOut 如果沒實現,就動態添加一下:
if (![_currentView respondsToSelector:@selector(moveOut:)]) {
BOOL sucess = class_addMethod([_currentView class], @selector(moveOut:), (IMP)moveOut, "i@:@");
使用的時候,如下:
if (!_slideView) {
PTVLiveBroadsideView *slideView = [PTVLiveBroadsideView new];
slideView.backgroundColor = [UIColor clearColor];
_slideView = slideView;
}
[targetController.view addSubview:_slideView];
_slideView.frame = targetController.view.bounds;
// PTVCommonShareView *sv = [PTVCommonShareView new];
// PTVDormancyView *sv = [[PTVDormancyView alloc] initWithViewModel:[PTVDornacyViewModel new]];
PTVVideoQualityView *sv = [[PTVVideoQualityView alloc] initWithViewModel:[PTVVideoQualityViewModel new]];
// PTVBarrageOptionView *sv = [[PTVBarrageOptionView alloc] initWithViewModel:[PTVBarrageViewModel new]];
[_slideView showView:(id<LiveBroadsideViewProtocol>)sv];
要彈出的view都不用實現moveIn
和 moveOut
方法,在 showView
方法裏已經動態添加過了。所以在 調用的使用纔會強制轉化 騙一下編譯器
(id<LiveBroadsideViewProtocol>)sv
當然,如果大小不一樣,可以擴展LiveBroadsideViewProtocol
協議。