简要
我们平时做SDK封装,可能会想用UITableView那样的设置数据源或代理的方式来做封装。
self.tableView.dataSource = self;
self.tableView.delegate = self;
这样做很好,我们可以自己封装一个。
需求点
昨天,我需要封装一个滚动号码球视图,像老虎机那种,它的子视图是由外部自定义的,我的想法是像tabelView自定义cell一样自定义子视图。
如何做
以我的SDK为例,我们先创建好数据源方法或代理方法
@protocol BTScrollBallDataSource <NSObject>
@optional
/// 要显示多少组号码
/// @param scrollBall 试图
- (NSInteger)numberOfSectionsInScrollBall:(BTScrollBallView *)scrollBall;
@end
@interface BTScrollBallView : UIView
/// 数据源方法
@property (weak,nonatomic) id<BTScrollBallDataSource> dataSource;
@end
@implementation BTScrollBallView
- (instancetype)init{
self = [super init];
if (self) {
self.backgroundColor = UIColor.greenColor;
[self createLayers]; //在此处调用方法创建视图
}
return self;
}
- (void)createLayers{
// 这里通过代理方法获取数据源个数
NSInteger sections = 0;
if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInScrollBall:)]) {
sections = [self.dataSource numberOfSectionsInScrollBall:self];
}
}
@end
认真观察一下
到这里你可能会认为已经可以了,可以获取到数据了。
但其实这里有一个很大的问题:这里根本无法拿到数据源对象,这里的self.dataSource
是为nil的。
在控制器UIViewController中使用一下看看:
@interface UIViewController()
@property (strong,nonatomic) BTScrollBallView *scrollBall;
@end
@implementation UIViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.scrollBall];
}
// MARK: - MKScrollBallDataSource
- (NSInteger)numberOfSectionsInScrollBall:(BTScrollBallView *)scrollBall{
return self.datas.count;
}
- (BTScrollBallView *)scrollBall{
if (!_scrollBall) {
_scrollBall = [[BTScrollBallView alloc]init];
_scrollBall.dataSource = self;
}
return _scrollBall;
}
@end
可以看到,我们调用完_scrollBall = [[BTScrollBallView alloc]init];
之后才调用设置数据源_scrollBall.dataSource = self;
,这个时候BTScrollBallView视图中init方法想通过数据源获取数据怎么可能有值呢?
那怎么办,难道要在初始化的时候把数据源dataSource
或delegate
传进去吗?这限制太死了,非常不合理,我们看看更好的方式。
正确的姿势
我们可以等视图已经完成添加到父视图之后再获取代理delegate
或dataSource
,此时的视图已经初始化创建完成了。这时再去获取数据源代理就能获取成功。
看看UIView的生命周期:
在UIView的生命周期有一条:didMoveToSuperView
方法,这个方法就是视图被添加到父视图完成后走的方法。
到这里就很明了了,我们在这个方法调用获取代理delgate或dataSource就没有问题了。
- (void)didMoveToSuperview{
[super didMoveToSuperview];
[self createLayers];
}
- (void)createLayers{
NSInteger sections = 0;
if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInScrollBall:)]) {
sections = [self.dataSource numberOfSectionsInScrollBall:self];
}
}
谢谢查阅!
详细代码:
BTScrollBallView
参考:
UIView生命周期
网友评论