开发App的时候,有的vc非常简单,有的vc就是非常复杂,我目前所在的项目中的VC就是非常复杂,前前后后有将近30多个view,在vc中view管理这些子view的时候就会导致vc极度的膨胀,将来业务扩展的时候,添加新的view的时候也有可能会添加的位置不对,导致view的管理混乱,因此有必要进行拆分。
也就是说有如下的两个问题:
- view的子view太多,导致view代码量增大,
- 新的业务需要新的view会导致添加的顺序不对。
借鉴了window的level概念,我们也初步设计了一个view的level概念。也就是说把这个复杂的view拆分几层,每个层级都有相关的子view。
结构图如下:
image.png刚开始设计的是每一个层级都是一个view,但是需要解决事件透传的问题(重写level view的hitTest方法),可以通过事件响应链能解决此问题。
在实际实现的过程中突然想起,我每一层不一定是需要一个view来装各种子view,我可以使用对象,一个占位view和- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview;
来实现同样的效果并且还不用重写view的hitTest方法。
具体的代码如下:
VKBaseLevel.h
:
@interface VKBaseLevel : NSObject
// 一个占位view,frame为zero
@property (nonatomic, readonly) UIView *placeholderView;
// 实际显示的view,不包括占位view
@property (nonatomic, readonly) NSArray *subviews;
// 此层级是否为空
@property (nonatomic, readonly) BOOL empty;
// 需要使用weak,要不然会出现循环引用了
@property (nonatomic, readonly, weak) UIView *superview;
// Need Override, 所有可能的view
@property (nonatomic, readonly) NSArray *possibleSubviews;
- (id)initWithSuperview:(UIView *)superview;
- (void)addSubview:(UIView *)view;
@end
VKBaseLevel.m
:
#import "VKBaseLevel.h"
@interface VKBaseLevel()
@property (nonatomic, readwrite, strong) UIView *placeholderView;
@property (nonatomic, readwrite, weak) UIView *superview;
@end
@implementation VKBaseLevel
- (id)initWithSuperview:(UIView *)superview {
if (self = [super init]) {
self.placeholderView = [UIView new];
self.superview = superview;
[superview addSubview:self.placeholderView];
}
return self;
}
- (void)addSubview:(UIView *)view {
[self.superview insertSubview:view belowSubview:self.placeholderView];
}
- (NSArray *)possibleSubviews {
return [NSArray new];
}
- (NSArray *)subviews {
NSMutableArray *mutAry = [NSMutableArray new];
for (UIView *view in self.possibleSubviews) {
if (view.superview != nil) {
[mutAry addObject:view];
}
}
return [mutAry copy];
}
- (BOOL)empty {
return self.subviews.count == 0;
}
@end
其中一个level的代码如下:
@interface VKBGLevel()
@property (nonatomic, strong) UILabel *pptLabel;
@property (nonatomic, strong) UILabel *ppt2Label;
@end
@implementation VKBGLevel
- (id)initWithSuperview:(UIView *)superview {
if (self = [super initWithSuperview:superview]) {
self.pptLabel = [UILabel new];
self.pptLabel.text = @"我是背景图";
self.pptLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.pptLabel];
self.ppt2Label = [UILabel new];
self.ppt2Label.text = @"我是背景图2";
self.ppt2Label.backgroundColor = [UIColor whiteColor];
self.ppt2Label.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.ppt2Label];
// 布局代码
}
return self;
}
- (NSArray *)possibleSubviews {
NSMutableArray *ary = [NSMutableArray new];
if (self.pptLabel) {
[ary addObject:self.pptLabel];
}
return [ary copy];
}
@end
最外层使用的:
@interface VKRaptorView() <VKNormalLevelDelegate>
@property (nonatomic, strong) VKBGLevel *bgLevel;
@property (nonatomic, strong) VKVideoLevel *videoLevel;
@property (nonatomic, strong) VKNormalLevel *normalLevel;
@property (nonatomic, strong) VKGuideLevel *guideLevel;
@property (nonatomic, strong) VKAnimationLevel *animationLevel;
@end
@implementation VKRaptorView
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.bgLevel = [[VKBGLevel alloc] initWithSuperview:self];
self.videoLevel = [[VKVideoLevel alloc] initWithSuperview:self];
self.normalLevel = [[VKNormalLevel alloc] initWithSuperview:self];
// 层级之间是通过delegate交互的
self.normalLevel.delegate = self;
self.animationLevel = [[VKAnimationLevel alloc] initWithSuperview:self];
self.guideLevel = [[VKGuideLevel alloc] initWithSuperview:self];
}
return self;
}
#pragma mark - VKNormalLevelDelegate
- (void)helpActionInNormalLevel:(VKNormalLevel *)bgLevel {
[self.guideLevel showHelp];
}
@end
重点是:每一层是一个对象,每个对象中含有一个默认的placeholderView,这个view相当于每层view的分割线。
这样就可以很好的管理一个复杂的view,给vc减负,将来有新来的view,放到指定的层级即可。
github:ViewLevel
事件响应链来解决此问题
直接上代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSArray *subviews = [self.subviews.reverseObjectEnumerator allObjects];
for (UIView *view in subviews) {
if (view.hidden || view.alpha < 0.1) {
continue;
}
BOOL inSubView = CGRectContainsPoint(view.frame, point);
if (inSubView) {
NSLog(@"YES: %@", [self class]);
// 千万不能是 return self; return self 是让这个类来处理所有的事件
return [super hitTest:point withEvent:event];
} else {
NSLog(@"NO: %@", [self class]);
}
}
return nil;
}
网友评论