iOS小技能:自定义tab滑块( segment功能+label混合显示)

简介: iOS小技能:自定义tab滑块( segment功能+label混合显示)

前言

需求:商户交易汇总表使用tab滑块进行切换/筛选不同级别的代理商数据

应用场景:商户交易汇总表使用tab滑块进行切换/筛选不同级别的代理商数据

视频地址:https://live.csdn.net/v/156406

下载Demo:https://download.csdn.net/download/u011018979/19790950

I、自定义tab滑块的用法

image.png

下载Demo:https://download.csdn.net/download/u011018979/19790950视频地址:https://live.csdn.net/v/156406

需求:商户交易汇总表使用tab滑块进行切换/筛选不同级别的代理商数据

应用场景:商户交易汇总表使用tab滑块进行切换/筛选不同级别的代理商数据

接口设计:如果数据比较大,就把统计和列表数据分开请求,这样可先展示列表数据,再展示统计数据

1.2 用法

  • 初始化控件
/**
 本级代理商数据
 下级代理商数据
 */
- (CRMMultipleSwitch *)MultipleSwitch{
    if (nil == _MultipleSwitch) {
        CRMMultipleSwitch *switch1 = [[CRMMultipleSwitch alloc]init];
        _MultipleSwitch = switch1;
        [self addSubview:_MultipleSwitch];
        __weak __typeof__(self) weakSelf = self;
        [[switch1 rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof CRMMultipleSwitch *  multipleSwitch) {
                NSLog(@"点击了第%zd个",multipleSwitch.selectedSegmentIndex);
        }];
        [switch1 mas_makeConstraints:^(MASConstraintMaker *make) {
           //        switch1.frame = CGRectMake(0, 0, 180, 30);
            make.height.mas_equalTo(kAdjustRatio(33));
            make.top.offset(kAdjustRatio(18));
            make.bottom.offset(kAdjustRatio(-18));
            make.left.offset(kAdjustRatio(74));
            make.right.offset(kAdjustRatio(-74));
        }];
    }
    //>推荐MultipleSwitch初始化的时候进行数据模型的赋值
    return _MultipleSwitch;
}
  • 设置模型数据

推荐MultipleSwitch初始化的时候进行数据模型的赋值

- (void)setModels:(CRMMultipleSwitchCellTableViewCellModel *)models{
    _models = models;
    if(models.items.count>0){
        self.MultipleSwitch.items= models.items;
  [self setupswitchStyle];
    }
}
- (CRMMerchantTransactionByPageMiddleViewModel *)viewModel{
    if (_viewModel == nil) {
        _viewModel = [CRMMerchantTransactionByPageMiddleViewModel new];
        self.viewModel.multipleSwitchCellTableViewCellModel = [CRMMultipleSwitchCellTableViewCellModel new];
//        @[@" 1",@"2 "];
        self.viewModel.multipleSwitchCellTableViewCellModel.items = @[@"本级代理商数据",@"下级代理商数据"];
    }
    return _viewModel;
}
  • 设置更新滑块样式
- (void)layoutSubviews {
    [super layoutSubviews];
    [self setupswitchStyle];
}
- (void)setupswitchStyle{
        [[self MultipleSwitch] layoutIfNeeded];
        CRMMultipleSwitch *switch1 = self.MultipleSwitch;
    //            switch1.layer.borderWidth = 1 / [UIScreen mainScreen].scale;
    //            switch1.layer.borderColor = [UIColor whiteColor].CGColor;
        switch1.layer.backgroundColor = [[UIColor colorWithRed:255.0f/255.0f green:122.0f/255.0f blue:144.0f/255.0f alpha:1.0f] CGColor];
                switch1.selectedTitleColor = [UIColor redColor];
        //        @property (nonatomic, copy) UIColor *trackerColor; // 滑块的颜色
        //        @property (nonatomic, copy) UIImage *trackerImage; // 滑块的图片
                switch1.titleColor = [UIColor whiteColor];
    switch1.titleFont = kPingFangFont(14);
                switch1.trackerColor = [UIColor whiteColor];
}

II、代码实现

CRMMultipleSwitch.h

NS_ASSUME_NONNULL_BEGIN
/**
 类似segment功能,label混合显示
 */
@interface CRMMultipleSwitch : UIControl
- (instancetype)initWithItems:(NSArray *)items;
@property(nonatomic) NSInteger selectedSegmentIndex;
@property (nonatomic, strong) UIColor  *titleColor;
@property (nonatomic, strong) UIColor  *selectedTitleColor;
@property (nonatomic, strong) UIFont   *titleFont;
@property (nonatomic, assign) CGFloat  spacing; // label之间的间距
@property (nonatomic, assign) CGFloat  contentInset; // 内容内宿边距
@property (nonatomic, copy) UIColor *trackerColor; // 滑块的颜色
@property (nonatomic, copy) UIImage *trackerImage; // 滑块的图片
@property (nonatomic, strong) NSArray *items;
// *)
@end

CRMMultipleSwitch.m

#import "CRMMultipleSwitch.h"
@interface SPMultipleSwitchLayer : CALayer
@end
@implementation SPMultipleSwitchLayer
- (instancetype)init {
    if (self = [super init]) {
        self.masksToBounds = YES;
    }
    return self;
}
- (void)setFrame:(CGRect)frame {
    [super setFrame:frame];
    self.cornerRadius = frame.size.height/2.0;
}
- (void)setCornerRadius:(CGFloat)cornerRadius {
    [super setCornerRadius:self.bounds.size.height/2.0];
}
+ (Class)layerClass{
    return [SPMultipleSwitchLayer class];
}
- (void)layoutSublayers {
    [super layoutSublayers];
    [self layoutIfNeeded];
    self.cornerRadius = self.frame.size.height/2.0;
}
@end
@interface CRMMultipleSwitch()
@property (nonatomic, strong) UIView *labelContentView;
@property (nonatomic, strong) UIView *selectedLabelContentView;
@property (nonatomic, strong) UIImageView *tracker;
@property (nonatomic, strong) NSMutableArray *labels;
@property (nonatomic, strong) NSMutableArray *selectedLabels;
@property (nonatomic, strong) UIView *maskTracker;
@property (nonatomic, assign) CGPoint beginPoint;
@end
@implementation CRMMultipleSwitch
+ (Class)layerClass{
    return [SPMultipleSwitchLayer class];
}
- (instancetype)initWithItems:(NSArray *)items {
    if (self = [self init]) {
        // 创建子控件
//        [self setupSubviewsWithItems:items];
        self.items = items;
//        [self.tracker addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
    }
    return self;
}
//- (NSArray *)setit
- (void)setItems:(NSArray *)items{
    _items = items;
    [self setupSubviewsWithItems:items];
}
//- (void)setupSubviewsWithItems{
    // 创建子控件
//}
- (instancetype)init {
    if (self = [super  init]) {
        _titleFont = [UIFont systemFontOfSize:17];
        _titleColor = [UIColor redColor];
        _selectedTitleColor = [UIColor whiteColor];
    }
    return self;
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (object == self.tracker) {
        if ([keyPath isEqualToString:@"frame"]) {
            self.maskTracker.frame = self.tracker.frame;
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
#pragma mark - UIControl Override
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    [super beginTrackingWithTouch:touch withEvent:event];
    _beginPoint = [touch locationInView:self];
    NSInteger index = [self indexForPoint:_beginPoint];
    self.selectedSegmentIndex = index;
    return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    [super continueTrackingWithTouch:touch withEvent:event];
    CGRect contentRect = CGRectInset(self.bounds, self.contentInset, self.contentInset);
    CGPoint currentPoint = [touch locationInView:self];
    CGFloat diff = currentPoint.x - _beginPoint.x;
    CGRect trackerFrame = self.tracker.frame;
    trackerFrame.origin.x += diff;
    trackerFrame.origin.x = MAX(MIN(CGRectGetMinX(trackerFrame),contentRect.size.width - trackerFrame.size.width), 0);
    self.tracker.frame = trackerFrame;
    _beginPoint = currentPoint;
    [self sendActionsForControlEvents:UIControlEventValueChanged];
    return YES;
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
    [super endTrackingWithTouch:touch withEvent:event];
    NSInteger index = [self indexForPoint:self.tracker.center];
    self.selectedSegmentIndex = index;
}
- (void)cancelTrackingWithEvent:(UIEvent *)event {
    [super cancelTrackingWithEvent:event];
    NSInteger index = [self indexForPoint:self.tracker.center];
    self.selectedSegmentIndex = index;
}
// 根据一个点,取出离该点最近的label对应的index
- (NSInteger)indexForPoint:(CGPoint)point {
    CGRect contentRect = CGRectInset(self.bounds, self.contentInset, self.contentInset);
    NSInteger index = MAX(0, MIN(_labels.count - 1, point.x / (contentRect.size.width / (CGFloat)(_labels.count))));
    return index;
}
#pragma mark - setter
- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex {
    _selectedSegmentIndex = MAX(0, MIN(selectedSegmentIndex, _labels.count-1));
    UILabel *label = _labels[_selectedSegmentIndex];
    CGPoint trackerCenter = self.tracker.center;
    trackerCenter.x = label.center.x;
    self.tracker.center = trackerCenter;
    // 这行代码主要是为了走一遍KVO监听的方法
    self.tracker.frame = self.tracker.frame;
}
- (void)setContentInset:(CGFloat)contentInset {
    _contentInset = contentInset;
    [self setNeedsLayout];
    [self layoutIfNeeded];
}
- (void)setSpacing:(CGFloat)spacing {
    _spacing = spacing;
    [self setNeedsLayout];
    [self layoutIfNeeded];
}
- (void)setTrackerColor:(UIColor *)trackerColor {
    _trackerColor = trackerColor;
    self.tracker.backgroundColor = _trackerColor;
}
- (void)setTrackerImage:(UIImage *)trackerImage {
    _trackerImage = trackerImage;
    self.tracker.image = _trackerImage;
}
- (void)setTitleColor:(UIColor *)titleColor {
    _titleColor = titleColor;
    [_labels setValue:_titleColor forKeyPath:@"textColor"];
}
- (void)setSelectedTitleColor:(UIColor *)selectedTitleColor {
    _selectedTitleColor = selectedTitleColor;
    [_selectedLabels setValue:_selectedTitleColor forKeyPath:@"textColor"];
}
- (void)setTitleFont:(UIFont *)titleFont {
    _titleFont = titleFont;
    [_labels setValue:_titleFont forKeyPath:@"font"];
    [_selectedLabels setValue:_titleFont forKeyPath:@"font"];
}
#pragma mark - 添加子控件
- (void)setupSubviewsWithItems:(NSArray *)items {
    if(self.labelContentView){
        [self.labelContentView removeFromSuperview];
    }
    //
    if(self.labels.count>0){
        [self.labels makeObjectsPerformSelector:@selector(removeFromSuperview)];
    }
    if(self.selectedLabels.count>0){
        [self.selectedLabels makeObjectsPerformSelector:@selector(removeFromSuperview)];
    }
    if(self.tracker){
        [self.tracker removeFromSuperview];
    }
//
    if(self.maskTracker){
        [self.maskTracker removeFromSuperview];
    }
    if(self.selectedLabelContentView){
        [self.selectedLabelContentView removeFromSuperview];
    }
    self.labels = [NSMutableArray array];
    self.selectedLabels = [NSMutableArray array];
    // 第一层
    {
        UIView *labelContentView = [[UIView alloc] init];
        labelContentView.userInteractionEnabled = NO;
        labelContentView.layer.masksToBounds = YES;
        [self addSubview:labelContentView];
        _labelContentView = labelContentView;
        for (int i = 0; i < items.count; i++) {
            UILabel *label = [[UILabel alloc] init];
            label.textAlignment = NSTextAlignmentCenter;
            label.text = items[i];
            label.textColor = [UIColor blackColor];
            [labelContentView addSubview:label];
            [self.labels addObject:label];
        }
        UIImageView *tracker = [[UIImageView alloc] init];
        tracker.userInteractionEnabled = NO;
        tracker.layer.masksToBounds = YES;
        tracker.backgroundColor = [UIColor redColor];
        [labelContentView addSubview:tracker];
        _tracker = tracker;
        [self.tracker addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
    }
    // 第二层
    {
        UIView *selectedLabelContentView = [[UIView alloc] init];
        selectedLabelContentView.userInteractionEnabled = NO;
        selectedLabelContentView.layer.masksToBounds = YES;
        [self addSubview:selectedLabelContentView];
        _selectedLabelContentView = selectedLabelContentView;
        for (int i = 0; i < items.count; i++) {
            UILabel *label = [[UILabel alloc] init];
            label.textAlignment = NSTextAlignmentCenter;
            label.text = items[i];
            label.textColor = [UIColor whiteColor];
            [selectedLabelContentView addSubview:label];
            [self.selectedLabels addObject:label];
        }
        UIView *maskTracker = [[UIView alloc] init];
        maskTracker.userInteractionEnabled = NO;
        maskTracker.backgroundColor = [UIColor redColor];
        _maskTracker = maskTracker;
        // 设置selectedLabelContentView的maskView,stackView是UIView的非渲染型子类,它无法设置backgroundColor,maskView等属性
        _selectedLabelContentView.maskView = maskTracker;
    }
}
- (void)layoutSubviews {
    [super layoutSubviews];
    CGRect contentRect = CGRectInset(self.bounds, self.contentInset, self.contentInset);
    self.labelContentView.frame = contentRect;
    self.selectedLabelContentView.frame = contentRect;
    self.labelContentView.layer.cornerRadius = contentRect.size.height / 2.0;
    self.selectedLabelContentView.layer.cornerRadius = contentRect.size.height / 2.0;
    CGFloat labelW =  (contentRect.size.width - _spacing * self.labels.count) / self.labels.count;
    [self.labels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger idx, BOOL * _Nonnull stop) {
        label.frame = CGRectMake(self->_spacing * 0.5 + idx * (labelW + self->_spacing) , 0, labelW, contentRect.size.height);
    }];
    [self.selectedLabels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger idx, BOOL * _Nonnull stop) {
        label.frame = CGRectMake(self->_spacing * 0.5 + idx * (labelW + self->_spacing), 0, labelW, contentRect.size.height);
    }];
    CGFloat averageWidth = contentRect.size.width / self.labels.count;
    self.tracker.frame = CGRectMake(_selectedSegmentIndex * averageWidth, 0, averageWidth, contentRect.size.height);
    self.tracker.layer.cornerRadius = contentRect.size.height / 2.0;
    self.maskTracker.layer.cornerRadius = contentRect.size.height / 2.0;
}
- (void)dealloc {
    [self.tracker removeObserver:self forKeyPath:@"frame"];
}
@end

see also

目录
相关文章
|
7月前
|
iOS开发
iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结
iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结
221 0
|
7月前
|
API iOS开发
iOS 自定义转场动画 UIViewControllerTransitioning
iOS 自定义转场动画 UIViewControllerTransitioning
48 0
|
8月前
|
Swift iOS开发
iOS 13 之后自定义 Window 不显示解决 (SceneDelegate)
iOS 13 之后自定义 Window 不显示解决 (SceneDelegate)
258 0
|
Linux iOS开发 开发者
WIN11自定义版本ios镜像下载教程
WIN11自定义版本ios镜像下载教程
WIN11自定义版本ios镜像下载教程
|
API iOS开发 Perl
iOS UISlider自定义渐变色滑杆
iOS UISlider自定义渐变色滑杆
iOS UISlider自定义渐变色滑杆
|
API iOS开发 Perl
iOS UILabel自定义位置
iOS UILabel自定义位置
iOS UILabel自定义位置
|
安全 Linux 网络安全
【IOS实用玩机技巧】爱思助手 IPA 签名功能常见问题汇总(iOS上架)
【IOS实用玩机技巧】爱思助手 IPA 签名功能常见问题汇总(iOS上架)
2286 0
【IOS实用玩机技巧】爱思助手 IPA 签名功能常见问题汇总(iOS上架)
iOS--设置系统导航栏右上角按钮不显示问题
iOS--设置系统导航栏右上角按钮不显示问题
196 0
|
iOS开发
iOS开发 - 点击tabbar某一个item,直接push跳转进入需要的页面,而不是切换tab
iOS开发 - 点击tabbar某一个item,直接push跳转进入需要的页面,而不是切换tab
372 0
|
移动开发 JavaScript weex
weex-自定义module,实现weex在iOS的本地化,js之间互相跳转,交互,传值(iOS接入weex的最佳方式)
weex-自定义module,实现weex在iOS的本地化,js之间互相跳转,交互,传值(iOS接入weex的最佳方式)
219 0