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

目录
相关文章
|
6月前
|
存储 数据建模 iOS开发
iOS设备功能和框架: 什么是 Core Data,它在 iOS 中的作用是什么?
iOS设备功能和框架: 什么是 Core Data,它在 iOS 中的作用是什么?
101 1
|
6月前
|
定位技术 iOS开发
iOS设备功能和框架: 如何使用 Core Location 获取设备的位置信息?
iOS设备功能和框架: 如何使用 Core Location 获取设备的位置信息?
75 0
|
6月前
|
iOS开发 UED
实现一个自定义的iOS动画效果
【4月更文挑战第9天】本文将详细介绍如何在iOS平台上实现一个自定义的动画效果。我们将通过使用Core Animation框架来实现这个动画效果,并展示如何在不同的场景中使用它。文章的目标是帮助读者理解如何使用Core Animation框架来创建自定义动画,并提供一个简单的示例代码。
53 1
|
22天前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
38 1
|
28天前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
49 3
|
1月前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
50 5
|
1月前
|
Swift iOS开发 UED
实现一个自定义的iOS动画效果
本文介绍如何使用Swift和UIKit在iOS应用中实现一个自定义按钮动画,当按钮被点击时,其颜色从蓝色渐变为绿色,形状从圆形变为椭圆形,释放后恢复原状。通过UIView动画方法实现这一效果,代码示例展示了动画的平滑过渡和状态切换,有助于提升应用的视觉体验和用户交互。
50 1
|
2月前
|
Swift iOS开发 UED
揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【9月更文挑战第5天】本文通过具体案例介绍如何在iOS应用中使用Swift与UIKit实现自定义按钮动画,当用户点击按钮时,按钮将从圆形变为椭圆形并从蓝色渐变到绿色,释放后恢复原状。文中详细展示了代码实现过程及动画平滑过渡的技巧,帮助读者提升应用的视觉体验与特色。
63 11
|
3月前
|
Swift iOS开发 UED
【绝妙创意】颠覆你的视觉体验!揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【8月更文挑战第13天】本文通过一个具体案例,介绍如何使用Swift与UIKit在iOS应用中创建独特的按钮动画效果。当按钮被按下时,其形状从圆形变化为椭圆形,颜色则从蓝色渐变为绿色;释放后,动画反向恢复原状。利用UIView动画方法及弹簧动画效果,实现了平滑自然的过渡。通过调整参数,开发者可以进一步优化动画体验,增强应用的互动性和视觉吸引力。
52 7
|
4月前
|
人工智能 搜索推荐 iOS开发
苹果发布iOS 18 Beta 4,新增CarPlay 壁纸等多项功能改进
本文首发于公众号“AntDream”,探索iOS 18 Beta 4新功能与改进: CarPlay壁纸、iCloud设置访问优化、相机控制记忆、隐藏文件夹设计变更、深色/浅色模式图标同步、股票应用图标调整、iPhone镜像功能增强、控制中心蓝牙切换键、AssistiveTouch新增Type to Siri等,以及Apple Intelligence暗示。开发者可通过苹果计划提前体验。
103 12