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
协议。