iOS 实现步骤进度条

作者: 独木舟的木 | 来源:发表于2018-03-16 17:17 被阅读44次

步骤进度条效果参考

iOS UIKit 框架中并没有提供类似的控件,我们可以使用 UIProgressView、UIView、UILabel 组合实现步骤进度条效果。

  • UIProgressView——实现水平的进度条效果;
  • UIView——把UIView裁剪成圆形,实现索引节点效果;
  • UILabel——每个节点下面的提示文字。

源码

将步骤进度条封装成一个 HQLStepView 类,它是 UIView 的子类。

HQLStepView.h 文件

#import <UIKit/UIKit.h>

@interface HQLStepView : UIView

// 指定初始化方法
- (instancetype)initWithFrame:(CGRect)frame titlesArray:(NSArray *)titlesArray stepIndex:(NSUInteger)stepIndex;

// 设置当前步骤
- (void)setStepIndex:(NSUInteger)stepIndex animation:(BOOL)animation;

@end

HQLStepView.m 文件

#import "HQLStepView.h"

// 步骤条主题色
#define TINT_COLOR [UIColor colorWithRed:35/255.f green:135/255.f blue:255/255.f alpha:1]

@interface HQLStepView ()

@property (nonatomic, copy) NSArray *titlesArray;
@property (nonatomic, assign) NSUInteger stepIndex;

@property (nonatomic, strong) UIProgressView *progressView;
@property (nonatomic, strong) NSMutableArray *circleViewArray;
@property (nonatomic, strong) NSMutableArray *titleLabelArray;
@property (nonatomic, strong) UILabel *indicatorLabel;

@end

@implementation HQLStepView

#pragma mark - Init

- (instancetype)initWithFrame:(CGRect)frame titlesArray:(NSArray *)titlesArray stepIndex:(NSUInteger)stepIndex {
    self = [super initWithFrame:frame];
    if (self) {
        _titlesArray = [titlesArray copy];
        _stepIndex = stepIndex;

        // 进度条
        [self addSubview:self.progressView];
        
        for (NSString *title in _titlesArray) {
            
            // 圆圈
            UIView *circle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 13, 13)];
            circle.backgroundColor = [UIColor lightGrayColor];
            circle.layer.cornerRadius = 13.0f / 2;
            [self addSubview:circle];
            [self.circleViewArray addObject:circle];
            
            // 标题
            UILabel *label = [[UILabel alloc] init];
            label.text = title;
            label.font = [UIFont systemFontOfSize:14];
            label.textAlignment = NSTextAlignmentCenter;
            [self addSubview:label];
            [self.titleLabelArray addObject:label];
        }
        
        // 当前索引数字
        [self addSubview:self.indicatorLabel];
    }
    return self;
}

// 布局更新页面元素
- (void)layoutSubviews {
    NSInteger perWidth = self.frame.size.width / self.titlesArray.count;
    
    // 进度条
    self.progressView.frame = CGRectMake(0, 0, self.frame.size.width - perWidth, 1);
    self.progressView.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 4);
    
    CGFloat startX = self.progressView.frame.origin.x;
    for (int i = 0; i < self.titlesArray.count; i++) {
        // 圆圈
        UIView *cycle = self.circleViewArray[i];
        if (cycle) {
            cycle.center = CGPointMake(i * perWidth + startX, self.progressView.center.y);
        }
        
        // 标题
        UILabel *label = self.titleLabelArray[i];
        if (label) {
            label.frame = CGRectMake(perWidth * i, self.frame.size.height / 2, self.frame.size.width / self.titlesArray.count, self.frame.size.height / 2 );
        }
    }
    self.stepIndex = self.stepIndex;
}

#pragma mark - Custom Accessors

- (UIProgressView *)progressView {
    if (!_progressView) {
        _progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
        _progressView.progressTintColor = TINT_COLOR;
        _progressView.progress = self.stepIndex / ((self.titlesArray.count - 1) * 1.0);
    }
    return _progressView;
}

- (UILabel *)indicatorLabel {
    if (!_indicatorLabel) {
        _indicatorLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 23, 23)];
        _indicatorLabel.textColor = TINT_COLOR;
        _indicatorLabel.textAlignment = NSTextAlignmentCenter;
        _indicatorLabel.backgroundColor = [UIColor whiteColor];
        _indicatorLabel.layer.cornerRadius = 23.0f / 2;
        _indicatorLabel.layer.borderColor = [TINT_COLOR CGColor];
        _indicatorLabel.layer.borderWidth = 1;
        _indicatorLabel.layer.masksToBounds = YES;
    }
    return _indicatorLabel;
}

- (NSMutableArray *)circleViewArray {
    if (!_circleViewArray) {
        _circleViewArray = [[NSMutableArray alloc] initWithCapacity:self.titlesArray.count];
    }
    return _circleViewArray;
}

- (NSMutableArray *)titleLabelArray {
    if (!_titleLabelArray) {
        _titleLabelArray = [[NSMutableArray alloc] initWithCapacity:self.titlesArray.count];
    }
    return _titleLabelArray;
}

// 设置当前进度索引,更新圆形图片、文本颜色、当前索引数字
- (void)setStepIndex:(NSUInteger)stepIndex {
    for (int i = 0; i < self.titlesArray.count; i++) {
        UIView *cycle = self.circleViewArray[i];
        UILabel *label = self.titleLabelArray[i];
        if (stepIndex >= i) {
            cycle.backgroundColor = TINT_COLOR;
            label.textColor = TINT_COLOR;
        } else {
            cycle.backgroundColor = [UIColor lightGrayColor];
            label.textColor = [UIColor lightGrayColor];
        }
    }
}

#pragma mark - Public

- (void)setStepIndex:(NSUInteger)stepIndex animation:(BOOL)animation {
    if (stepIndex < self.titlesArray.count) {
        // 更新颜色
        self.stepIndex = stepIndex;
        // 设置进度条
        [self.progressView setProgress:stepIndex / ((self.titlesArray.count - 1) * 1.0) animated:animation];
        // 设置当前索引数字
        self.indicatorLabel.text = [NSString stringWithFormat:@"%lu", stepIndex + 1];
        self.indicatorLabel.center = ((UIView *)[self.circleViewArray objectAtIndex:stepIndex]).center;
    }
}

@end

接口调用:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化
    _hqlStepView = [[HQLStepView alloc] initWithFrame:CGRectMake(0, 200, self.view.frame.size.width, 60) titlesArray:@[@"第一步", @"第二步", @"第三步"] stepIndex:0];
    [self.view addSubview:_hqlStepView];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // 设置当前步骤,步骤索引=数组索引
    [_hqlStepView setStepIndex:0 animation:YES];
}

效果:

因为 UIProgressView 实现的水平进度条高度值默认为1,设置frame是无效的。可以通过仿射变换的方式增加它的高度。

第三方框架

参考:

相关文章

网友评论

  • 怀念裸奔的童年:我在想已经有那么好的轮子了,为啥还要造这轮子。有啥特殊的地方吗
    独木舟的木:没有特殊的地方,而且这个轮子最最最最简单,适合用来练习理解用的,如果你想放到生产环境中去的话建议选择Star高的、集成度高的轮子使用。另外,虽然有一句话说,Don`t repear yourself。告诉我们说不要重复做相同的事情,我的理解是不要在业务中写重复的代码,可复用的可抽象的逻辑我们可以抽象出来复用(高内聚、低耦合)。然后开源社区有一大堆完善可使用的框架,你直接拿来用也没有问题,但是当你想要理解其中源码的时候,经常会有人写个XXX源码解析、撸一串相同的代码玩玩,就是为了理解其中的原理,所以有时候重复造轮子也不见得是坏处,写代码提高的关键其实也就是repeat、repeat、repeat,不断的重复练习,当你写满10000小时的代码时,那么,恭喜你,你应该已经是大神了。大抵就是这样。祝好。
  • JackerooChu:NSInteger perWidth = self.frame.size.width / self.titlesArray.count;
    这个宽度用NSInteger不太好吧
    独木舟的木:@JackerooChu 然后呢?
    JackerooChu:@独木舟的木 这个self.frame.width/self.titleArray.count,取到的不是整数么?
    独木舟的木:You usually want to use NSInteger when you don't know what kind of processor architecture your code might run on, so you may for some reason want the largest possible int type, which on 32 bit systems is just an int, while on a 64-bit system it's a long.

    NSInteger 与 int 的区别是 NSInteger 会根据系统的位数(32 or 64)自动选择 int 的最大数值(int or long)。如果你觉得总是选最大的数值会浪费内存的话,你也可以选择使用 int 或者 short。

本文标题:iOS 实现步骤进度条

本文链接:https://www.haomeiwen.com/subject/mqjrqftx.html