iOS-关于自定义分段选择器的一些小事(Segmented)

摘要:
系统内置的分段选择是UISegmentedControl。还有一些大人物定制的SegmentedControl,例如Git上的HMSegmentedControl。我也在以前的初始项目中使用过它。如果我编写或想要自定义一些UI,我应该从哪里开始?事实上,在使用HMSegmentedControl之后,我有了一个大致的想法。如果我想简单地实现它,我可以使用UICollectionView来实现它

系统自带的分段选择就是 UISegmentedControl ,也有一些大佬自定义的 Segmented ,比如Git上的 HMSegmentedControl ,我以前最初的项目中,也有用到过,如果自己写,或者想自定义一些UI,该从哪里出发,其实在用过 HMSegmentedControl 之后,大致就有思路了,如果想简单的实现下,可以利用 UICollectionView 来实现,下面是我利用 UICollectionView 写的一个简单的小栗子,效果图

iOS-关于自定义分段选择器的一些小事(Segmented)第1张

设计思路

iOS-关于自定义分段选择器的一些小事(Segmented)第2张

首先利用 UICollectionView 处理每个item的大小,确切的说是宽度,那么就要每次选中一个item后,重新计算所有的item的宽度,同时计算 UICollectionView 的 contentSize.width;

计算每个item宽度分为两种情况,一种是选中的字体的显示,一种是未选中的字体显示,比如字体大小,颜色等,然后根据字体大小字符串长度,计算出字体需要展示的宽度,并计算对应的item宽度,最后把每个item的宽度保存起来,用于在 UICollectionView 代理方法中做处理;

计算 contentSize.width 由上图可知,是由两边的间距,item之间的间距和每个item的总和,目的是利用 contentSize.width 计算下划线的位置;

具体实现

#import "XKCollectionView.h"

///四周边距
const static CGFloat _margin_left = 5;
const static CGFloat _margin_right = 5;
const static CGFloat _margin_top = 0;
const static CGFloat _margin_bottom = 2;
const static CGFloat _margin_space = 15;

const static CGFloat _line_width = 30.0;
const static CGFloat _line_height = 3.0;

@interface XKCollectionView ()<UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>
///临时数据
@property (nonatomic,strong) NSArray<NSString *> *titleArray;
///每个item的宽度
@property (nonatomic,strong) NSMutableArray *widthsArray;
///底部线条
@property (nonatomic,strong) UIView *lineView;
///选中的item索引
@property (nonatomic,assign) NSInteger selectIndex;
///选中的item string
@property (nonatomic,strong) NSString *selectString;
////计算出来的总宽度,用于设置 UICollectionView.contentSize.width
@property (nonatomic,assign) CGFloat totalContentWidth;
@end
@implementation XKCollectionView
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout{
    self = [super initWithFrame:frame collectionViewLayout:layout];
    if (self) {
        [self setup];
    }
    return self;
}
- (void)setup{
    _selectIndex = 0;
    self.widthsArray = [NSMutableArray array];
    [self addSubview:self.lineView];
    self.backgroundColor = [UIColor whiteColor];
    self.showsHorizontalScrollIndicator = NO;
    self.delegate = self;
    self.dataSource = self;
    [self registerClass:[XKCollectionViewCell class] forCellWithReuseIdentifier:@"XKCollectionViewCell"];
    
    _titleArray = @[@"一级建造师",@"二级建造师",@"造价工程师",@"咨询工程师",@"注册安全工程师",@"监理工程师",@"注册电气工程师",@"环境影响评价工程师",@"注册城乡规划师",@"注册消防工程师"];
    [self storeSegmentedWidth];
    [self reloadData];
    CGRect lineRext = [self measureLineFrame];
    self.lineView.frame = lineRext;
    ///设置偏移量
    [self setContentOffset:CGPointMake([self measureContentOffsetX], 0)];
}


- (void)updateSelectSeg{
    [self storeSegmentedWidth];
    [self reloadData];
    [UIView animateWithDuration:0.3 animations:^{
        CGRect lineRext = [self measureLineFrame];
        self.lineView.frame = lineRext;
    }];
    
    [self setContentOffset:CGPointMake([self measureContentOffsetX], 0) animated:YES];
}
#pragma mark ========== 储存计算好的item宽度 ==========
///每次切换时更新
- (void)storeSegmentedWidth{
    _selectIndex = 0;
    _totalContentWidth = 0;
    [self.widthsArray removeAllObjects];
    
    if (_selectString) {
        for (int i = 0; i < _titleArray.count; i ++) {
            NSString *title = _titleArray[i];
            if ([title isEqualToString:_selectString]) {
                _selectIndex = i;
                break;
            }
        }
    }
   
    
    for (int i = 0; i < _titleArray.count; i ++) {
        
        CGSize size = [self measureTitleIndex:i];
        NSNumber *value = [NSNumber numberWithFloat:size.width];
        [self.widthsArray addObject:value];
        
        _totalContentWidth = _totalContentWidth + size.width;
        if (i < _titleArray.count - 1) {
            _totalContentWidth = _totalContentWidth + _margin_space;
        }
    }
    _totalContentWidth = _totalContentWidth + _margin_left + _margin_right;
    
}
- (CGSize)measureTitleIndex:(NSUInteger)index {
    if (index >= _titleArray.count) {
        return CGSizeZero;
    }
    
    id title = _titleArray[index];
    CGSize size = CGSizeZero;
    BOOL selected = (index == _selectIndex);
    NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
    size = [(NSString *)title sizeWithAttributes:titleAttrs];
    UIFont *font = titleAttrs[@"NSFont"];
    size = CGSizeMake(ceil(size.width), ceil(size.height - font.descender));
    CGSize resault = CGRectIntegral((CGRect){CGPointZero, size}).size;
    return resault;
}
- (NSDictionary *)resultingSelectedTitleTextAttributes {
    NSDictionary *resultingAttrs = @{NSForegroundColorAttributeName : [UIColor blackColor] ,NSFontAttributeName:[UIFont fontWithName:@"Helvetica-Bold" size:18.0]};
    return resultingAttrs;
}
- (NSDictionary *)resultingTitleTextAttributes {
    NSDictionary *resultingAttrs = @{NSForegroundColorAttributeName : [UIColor lightGrayColor],NSFontAttributeName:[UIFont systemFontOfSize:14.0]};
    return resultingAttrs;
}
#pragma mark ========== 计算下划线位置 ==========
- (CGRect)measureLineFrame{
    CGRect lineRect = CGRectZero;
    CGFloat lineRectX = 0;
    for (int i = 0; i < _selectIndex; i ++) {
        NSNumber *number = self.widthsArray[i];
        lineRectX = lineRectX + [number floatValue] + _margin_space;
    }
    CGFloat widthSelect = [self.widthsArray[_selectIndex] floatValue];
    CGFloat lastLocation = widthSelect >= _line_width ? (widthSelect - _line_width)/2 : (_line_width - widthSelect)/2;
    lineRectX = lineRectX + _margin_left + lastLocation;
    
    lineRect = CGRectMake(lineRectX, self.bounds.size.height - _line_height - 2, _line_width, _line_height);
    return lineRect;
}
#pragma mark ========== 计算偏移量 ==========
- (CGFloat)measureContentOffsetX{
    CGFloat selfWidth = self.bounds.size.width;
    
    ///先计算点击的item中心点
    CGFloat selectedCenterX = 0;
    for (int i = 0; i < _selectIndex; i ++) {
        NSNumber *number = self.widthsArray[i];
        selectedCenterX = selectedCenterX + [number floatValue] + _margin_space;
    }
    CGFloat widthSelect = [self.widthsArray[_selectIndex] floatValue];
    selectedCenterX = selectedCenterX + widthSelect/2;
    
    if (_totalContentWidth <= selfWidth) {///充满内部不做偏移
        return 0;
    }
    
    if (selectedCenterX <= selfWidth/2) {
        return 0;
    }
    else if (selectedCenterX >= _totalContentWidth - selfWidth/2){
        return _totalContentWidth - selfWidth;
    }
    else{
        return selectedCenterX - selfWidth/2;
    }
}
#pragma mark ========== 代理 ==========
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return _titleArray.count;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{
    return _margin_space;
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsMake(_margin_top, _margin_left, _margin_bottom, _margin_right);
}
//item大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    NSNumber *number = self.widthsArray[indexPath.row];
    return CGSizeMake([number floatValue],30);
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    XKCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"XKCollectionViewCell" forIndexPath:indexPath];
    NSString *title = _titleArray[indexPath.row];
    cell.title = title;
    if (indexPath.row == _selectIndex) {
        cell.isSelectd = YES;
    }
    else{
        cell.isSelectd = NO;
    }
    return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    _selectString = _titleArray[indexPath.row];
    [self updateSelectSeg];
}
#pragma mark ========== 变量 ==========

- (UIView *)lineView{
    if(!_lineView){
        _lineView = [[UIView alloc]init];
        _lineView.backgroundColor = [UIColor purpleColor];
        _lineView.layer.masksToBounds = YES;
        _lineView.layer.cornerRadius = _line_height/2;
    }
    return _lineView;
}

@end

免责声明:文章转载自《iOS-关于自定义分段选择器的一些小事(Segmented)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇解方程AX=b与矩阵分解:奇异值分解 特征值分解 QR分解 三角分解 Cholesky分解三段式状态机的思维陷阱下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

如何解决PC端和移动端自适应问题?

   做网页时,我们通常需要考虑到不同电脑屏幕尺寸,以及不同手机屏幕大小等问题,解决样式发生改变的情况,那么如何解决呢?现在主要是采用自适应来解决高度,宽度的,以及图片自适应问题,下面就PC端和移动端来总结一下,通常进行自适应高度和宽度,图片时,一般与页面的布局存在关系。 1、最小尺寸分辨率1024*768(传统17寸显示器),则可以采用940px、960...

vue 移动端车牌键盘

1.npm install input-plate-number --save 样式可以,键盘相对严谨,但是输入有bug, 2.npm installvue-carplate --save 插件样式个人看来是最好看的,键盘的输入最严谨,但是没有新能源车牌的选项, 3.https://github.com/Pinenutss/LicensePlate 插件是...

CSS 公共样式摘自万能的度娘

global.css | reset.css(格式化样式) common.css(公共组件样式) layout.css(当前页面样式) 清除全站所有页面的浏览器默认样式,保证在初始样式在所有浏览器下一致。 common.css(公共组件样式) 一般一个网站所有页面头部、底部样式都是一致的,而且很长时间不会有大的改变,改变的大概就是产品、运营的经常需要添加、...

Android _优雅实现元素间的分割线 (支持3.0以下)

转:http://blog.csdn.net/lmj623565791/article/details/42407923 1、概述 话说,随着Android SDK版本的升级,很多控件增加了新的属性方便我们的使用,比如LinearLayout中多了:divider、showDividers等,用于为其内部元素添加分隔;但是呢,这样的属性在较低版本的SDK中...

4、HTML和CSS进阶知识

HTML和CSS进阶知识 一、HTML中常用的块元素(block)和行内元素(inline)   常见的块元素(block):p、h1--h6、div、ul、ol、dl、hr   常见的行内元素(inline):b、i、em、strong、small、sub、sup、a、span、br、   在css中,可以通过display属性实现block元素和inl...

JS实现“隐藏与显示”功能(多种方法)

1,通过按钮实现隐藏与显示: 这个是通过按钮点击实现的隐藏与显示,具体代码如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47...