OC开发iOSiOS Development

iOS利用layoutSubviews, drawRect效率方

2017-02-22  本文已影响820人  Kobe_Dai

在app的开发过程当中,我们会根据业务需求,封装一些UI的控件,在写控件的过程中,我们经常会遇到这样的问题,在什么方法里导入控件需要的数据,在什么方法里对数据进行计算,在什么方法里绘制控件,那么这篇文章就讲一下我对一个UI控件构建的流程和理解

一个UI控件的构成总共分为4步:

下面我用一个常见的可横滑切换的SegmentedControl这个UI控件来解释这四个步骤应该怎么实现,效果图如下:

WechatIMG62.jpeg

首先我们创建这个控件,叫SegmentedControl,基于UIControl,这个控件由一个UIScrollView跟多个CATextLayer组成

1. 导入数据,初始化控件

利用init方法创建控件,并导入控件所需的数据

- (id)initWithConfig:(SegmentedControlConfig *)config
{
    if (self = [super init]) {
        self.config = config;
        
        [self initSegmentControl];
    }
}

- (void)initSegmentControl
{
    // 初始化该控件所需的properties
    // 初始化UIScrollView
}

2. 根据控件绘制的需求,对数据进行计算

在一个控件里,我们会用layoutSubviews来进行数据计算,layoutSubviews将会在设置控件frame(非CGRectZero) 的时候被调用。官方给出的layoutSubviews解释是Subclasses can override this method as needed to perform more precise layout of their subviews,由于所有的CATextLayer的segments跟UIScrollView都是该控件的subviews,所以我们使用layoutSubviews来根据提供的数据计算它们的layouts

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    [self updateSegmentsLayout];
}

- (void)updateSegmentsLayout
{
    // 根据self.config计算所有的CATextLayer segment的宽度跟高度,并用两个array保存以便绘制时使用
    // 根据总宽度计算UIScrollView的frame跟contentSize
}

补充说明下layoutSubviews的调用机制,在这几种情况下会被调用:

3. 绘制控件

在一个控件里,我们会用drawRect:来绘制控件里所有的views, 控件在第一次displayed的时候会调用drawRect

- (void)drawRect:(CGRect)rect
{
    // 根据updateSegmentsLayout方法里计算得到的segments宽度高度的数据,来绘制全部,上海,北京 etc. 的CATextLayer segments
}

补充说明下drawRect的调用机制,在这几种情况下会被调用:

4. 根据新的数据刷新控件

由于我们用layoutSubviewsdrawRect两个方法来计算,绘制控件,那么用新数据来刷新控件的方法会非常的简单并清晰,我们只需在获取新数据之后手动调用layoutSubviewsdrawRect这两个方法即可

- (void)reload:(SegmentedConfig *)config
{
    self.config = config;
    
    [self setNeedsLayout];    // call layoutSubviews
    [self setNeedsDisplay];   // call drawRect
}

5. 在业务层创建这个控件

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self.view addSubview: self.segmentedControl];
}

- (SegmentControl *)segmentedControl
{
    if (!_segmentedControl) {
        _segmentedControl = [[SegmentedControl alloc] initWithConfig:self.config];
        _segmentedControl.frame = CGRectMake(0, 0, self.view.frame.size.width, 60); // will call layoutSubviews
    }
    return _segmentedControl;
}

控件完整代码如下:

- (id)initWithConfig:(SegmentedControlConfig *)config
{
    if (self = [super init]) {
        self.config = config;
        
        [self initSegmentControl];
    }
}

- (void)initSegmentControl
{
    // 初始化该控件所需的properties
    // 初始化UIScrollView
}

- (void)updateSegmentsLayout
{
    // 根据self.config计算所有的CATextLayer segment的宽度跟高度,并用两个array保存以便绘制时使用
    // 根据总宽度计算UIScrollView的frame跟contentSize
}

- (void)reload:(SegmentedConfig *)config
{
    self.config = config;
    
    [self setNeedsLayout];    // call layoutSubviews
    [self setNeedsDisplay];   // call drawRect
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    [self updateSegmentsLayout];
}

- (void)drawRect:(CGRect)rect
{
    // 根据updateSegmentsLayout方法里计算得到的segments宽度高度的数据,来绘制全部,上海,北京 etc. 的CATextLayer segments
}

总结下,创建一个控件的正确步骤是这样的:init -> layoutSubviews -> drawRect:

跟SegmentedControl这个控件配套的是下面可以横滑的PagerViewController的一整套控件,跟网易新闻的类似,近期会整理出来会放GitHub上

转载请注明出处,原文地址:http://kobedai.me/p9rsts-6h/

上一篇下一篇

猜你喜欢

热点阅读