欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-03-18 16:45:11
系统自带的分段选择就是 UISegmentedControl ,也有一些大佬自定义的 Segmented ,比如Git上的 HMSegmentedControl ,我以前最初的项目中,也有用到过,如果自己写,或者想自定义一些UI,该从哪里出发,其实在用过 HMSegmentedControl 之后, ......

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

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

 

设计思路

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

 

首先利用 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