1.分段控制在App开发中是非常常见的,也有很多类似的demo实现的方式都是大同小异有UIScrollView加UIButton形式,UICollectionView形式,本文主要是UICollectionView的形式
实现方式
- 创建BaseView作为父类,在父类中添加可配置的属性如:颜色(选中和未选中),字体的大小,是否需要线条,等
- 继承父类创建对应的子类View,子类的view就是我们在开发中的分段控制的view,同时通过UICollectionView和自动化布局来实现,
直接上代码
1.OCSegmentBaseView
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, OCSegmentIndicatorViewAlign) {
OCSegmentIndicatorViewAlignCenter = 0, // 局中对其
OCSegmentIndicatorViewAlignLeft, // 左标题对其
};
@interface OCSegmentBaseView : UIView
// 标题颜色 默认:灰色 black
@property (nonatomic, strong) UIColor *titleColor;
// 标题选中颜色 默认:红色 red
@property (nonatomic, strong) UIColor *titleselectedColor;
// 标题颜色 默认:灰色 black
@property (nonatomic, strong) UIColor *subTitleColor;
// 标题选中颜色 默认:红色 red
@property (nonatomic, strong) UIColor *subTitleSelectedColor;
// 小线条颜色 默认:红色 red
@property (nonatomic, strong) UIColor *indicatorColor;
//正常的字体大小
@property (nonatomic, strong) UIFont *titleFont;
//选择的字体大小
@property (nonatomic, strong) UIFont *titleSelectedFont;
//子标题的字体大小
@property (nonatomic, strong) UIFont *subTitleFont;
//子标题选择的字体大小
@property (nonatomic, strong) UIFont *subTitleSelectedFont;
//第一个按钮到左边的距离
@property (nonatomic, assign) CGFloat leftPadding;
//两个按钮间的距离
@property (nonatomic, assign) CGFloat spacing;
//最后一个按钮到右边的距离
@property (nonatomic, assign) CGFloat rightPadding;
//是否显示下方的线 默认显示
@property (nonatomic, assign) BOOL indicatorShow;
//指示器线的高度
@property (nonatomic, assign) CGFloat indicatorHeight;
//指示器线的宽度
@property (nonatomic, assign) CGFloat indicatorWidth;
//默认的index
@property (nonatomic, assign) NSInteger defaultIndex;
//是否等份,默认是NO,(在一个指定宽中度实现等份)
@property (nonatomic, assign) BOOL equalParts;
//指示器的圆角剪切
@property (nonatomic, assign) CGFloat cornerRadius;
//动画的持续时间
@property (nonatomic, assign) CGFloat duration;
//indicatorView 对其方式
@property (nonatomic, assign) OCSegmentIndicatorViewAlign align;
@end
NS_ASSUME_NONNULL_END
#import "OCSegmentBaseView.h"
@implementation OCSegmentBaseView
- (instancetype)initWithFrame:(CGRect)frame {
if(self = [super initWithFrame:frame])
{
self.indicatorShow = YES;
self.align = OCSegmentIndicatorViewAlignCenter;
self.titleFont = [UIFont systemFontOfSize:14];
self.subTitleFont = [UIFont systemFontOfSize:12];
self.titleSelectedFont = [UIFont systemFontOfSize:15];
self.subTitleSelectedFont = [UIFont systemFontOfSize:12];
self.leftPadding = 0;
self.rightPadding = 0;
self.spacing = 0;
self.indicatorHeight = 2;
self.defaultIndex = 0;
self.equalParts = NO;
self.duration = 0.3;
self.titleColor = [UIColor blackColor];
self.titleselectedColor = [UIColor redColor];
self.subTitleColor = [UIColor blackColor];
self.subTitleSelectedColor = [UIColor redColor];
}
return self;
}
@end
2.OCSegmentTitleView
#import <UIKit/UIKit.h>
#import "OCSegmentBaseView.h"
@class OCSegmentTitleView;
@protocol OCSegmentTitleViewDelegate <NSObject>
@optional
- (void)segmentView:(OCSegmentTitleView *)segmentView didSelectItemAtIndex:(NSInteger)index;
@end
@interface OCSegmentTitleView : OCSegmentBaseView
//标题
@property (nonatomic, strong) NSArray<NSString *> *titleArray;
//子标题
@property (nonatomic, strong) NSArray<NSString *> *subTitleArray;
//指示器的View
@property (nonatomic, strong) UIView *indicatorView;
//选择的下标 只读的
@property (nonatomic, assign, readonly) NSInteger selectedIndex;
@property (nonatomic, weak) id<OCSegmentTitleViewDelegate> delegate;
@property (nonatomic, copy) void(^didSelectItemAtIndexBlock)(NSInteger index);
@property (nonatomic, copy) void(^didSelectItemAtIndexAndValueBlock)(NSInteger index, NSString *indexValue);
- (void)reloadData;
@end
#import "OCSegmentTitleView.h"
#import "OCSegmentTitleCCell.h"
@interface OCSegmentTitleView ()
<
UICollectionViewDataSource,
UICollectionViewDelegate,
UICollectionViewDelegateFlowLayout
>
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSIndexPath *indexPath;
//YES 第一次出现,NO 不是
@property (nonatomic, assign) BOOL firstAppear;
@property (nonatomic, strong) UICollectionViewFlowLayout *flowLayout;
@end
@implementation OCSegmentTitleView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame])
{
self.firstAppear = YES;
self.indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self addSubview:self.collectionView];
[self.collectionView addSubview:self.indicatorView];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.right.bottom.equalTo(self).offset(0);
}];
}
return self;
}
#pragma mark -- setter
@synthesize defaultIndex = _defaultIndex;
- (void)setDefaultIndex:(NSInteger)defaultIndex {
_defaultIndex = defaultIndex;
//默认的indexPath
self.indexPath = [NSIndexPath indexPathForRow:self.defaultIndex inSection:0];
}
- (NSInteger)selectedIndex {
return self.indexPath.row;
}
#pragma mark -- private Method
- (void)reloadData {
//可以立即获得到frame
[self layoutIfNeeded];
self.firstAppear = YES;
[self.collectionView reloadData];
//设置边距 top,left,bottom,right
self.flowLayout.sectionInset = UIEdgeInsetsMake(0, self.leftPadding, self.indicatorHeight, self.rightPadding);
//线条颜色
self.indicatorView.backgroundColor = self.indicatorColor;
//剪切圆角
self.indicatorView.layer.cornerRadius = self.cornerRadius;
if(self.titleArray.count > 0 && self.titleArray.count > self.indexPath.row)
{
[self.collectionView scrollToItemAtIndexPath:self.indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
else
{ //设置为0 没有标题,或者 indexPath.row 大于等于 数组的数量
self.indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
}
[self.collectionView reloadData];
}
#pragma mark -- UICollectionViewDataSource,UICollectionViewDelegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.titleArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
OCSegmentTitleCCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"collectionCell" forIndexPath:indexPath] ;
cell.titleLabel.text = self.titleArray[indexPath.item];
if(self.subTitleArray.count > 0 && self.subTitleArray.count > indexPath.item)
{//子标题赋值
cell.subTitleLabel.text = self.subTitleArray[indexPath.item];
}
if(self.indexPath == indexPath )
{//当前点击的
cell.titleLabel.font = self.titleSelectedFont;
cell.titleLabel.textColor = self.titleselectedColor;
cell.subTitleLabel.font = self.subTitleSelectedFont;
cell.subTitleLabel.textColor = self.subTitleSelectedColor;
if(self.indicatorShow)
{//显示--indicatorView
[self indicatorViewPosition:cell cellForItemAtIndexPath:indexPath];
}
}
else
{//其他未点击的
cell.titleLabel.font = self.titleFont;
cell.titleLabel.textColor = self.titleColor;
cell.subTitleLabel.font = self.subTitleFont;
cell.subTitleLabel.textColor = self.subTitleColor;
}
return cell ;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if(self.indexPath == indexPath)
{//点击已经选中的不做处理了
return;
}
self.firstAppear = NO;
self.indexPath = indexPath;
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
[self.collectionView reloadData];
if([self.delegate respondsToSelector:@selector(segmentView:didSelectItemAtIndex:)])
{
[self.delegate segmentView:self didSelectItemAtIndex:indexPath.row];
}
if(self.didSelectItemAtIndexBlock)
{
self.didSelectItemAtIndexBlock(indexPath.row);
}
if(self.didSelectItemAtIndexAndValueBlock)
{
self.didSelectItemAtIndexAndValueBlock(indexPath.row, self.titleArray[indexPath.row]);
}
}
#pragma mark - UICollectionViewDelegateFlowLayout
//返回cell的大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if(self.equalParts == YES)
{//平均分段
CGFloat width = (self.frame.size.width - (self.titleArray.count - 1)*self.spacing) /self.titleArray.count;
return CGSizeMake(width, self.frame.size.height - self.indicatorHeight);
}
else
{
CGSize size;
CGSize subSize;
if(self.indexPath.row == indexPath.row)
{
if(self.subTitleArray.count > 0 && self.subTitleArray.count > indexPath.row)
{
subSize = [self labelFont:self.subTitleSelectedFont textString:self.subTitleArray[indexPath.row]];
}
size = [self labelFont:self.titleSelectedFont textString:self.titleArray[indexPath.row]];
}
else
{
if(self.subTitleArray.count > 0 && self.subTitleArray.count > indexPath.row)
{
subSize = [self labelFont:self.subTitleFont textString:self.subTitleArray[indexPath.row]];
}
size = [self labelFont:self.titleFont textString:self.titleArray[indexPath.row]];
}
return CGSizeMake(size.width + subSize.width, self.frame.size.height - self.indicatorHeight);
}
}
//水平方向间距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return self.spacing;
}
//水平方向间距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}
#pragma mark -- private method
- (CGFloat)labefont:(UIFont *)font textArray:(NSArray *)textArray indexpath:(NSIndexPath *)indexPath {
CGSize size;
if(textArray.count > 0 && textArray.count > indexPath.item)
{
size = [self labelFont:font textString:textArray[indexPath.item]];
}
return size.width;
}
/* 根据文字长度返回size */
- (CGSize)labelFont:(UIFont *)font textString:(NSString *)str {
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, MAXFLOAT, 0)];
label.text = str;
label.numberOfLines = 0;
label.font = font;
//让label通过文字设置size
[label sizeToFit];
//获取label 的size
CGSize size = label.frame.size;
//返回出去
return size;
}
/* 指示器View的位置显示 */
- (void)indicatorViewPosition:(OCSegmentTitleCCell *)cell cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat lineWidth;
__weak typeof(self) weakSelf = self;
if(self.align == OCSegmentIndicatorViewAlignLeft)
{//左标题对其
CGFloat subWidth = [self labefont:self.subTitleSelectedFont textArray:self.subTitleArray indexpath:indexPath];
lineWidth = self.indicatorWidth > 0 ? self.indicatorWidth : cell.frame.size.width - subWidth;
}
else
{
lineWidth = self.indicatorWidth > 0 ? self.indicatorWidth : cell.frame.size.width;
}
if(self.firstAppear == YES)
{//第一次出现
self.indicatorView.frame = CGRectMake(0, self.frame.size.height - self.indicatorHeight, lineWidth, self.indicatorHeight);
if(self.align == OCSegmentIndicatorViewAlignLeft)
{//左标题对其
CGFloat subWidth = [self labefont:self.subTitleSelectedFont textArray:self.subTitleArray indexpath:indexPath];
self.indicatorView.center = CGPointMake(cell.center.x - subWidth/2, self.frame.size.height - self.indicatorHeight);
}
else
{
self.indicatorView.center = CGPointMake(cell.center.x, self.frame.size.height - self.indicatorHeight);
}
}
else
{
[UIView animateWithDuration:self.duration animations:^{
weakSelf.indicatorView.frame = CGRectMake(0, weakSelf.frame.size.height - weakSelf.indicatorHeight, lineWidth, weakSelf.indicatorHeight);
if(weakSelf.align == OCSegmentIndicatorViewAlignLeft)
{//左标题对其
CGFloat subWidth = [weakSelf labefont:weakSelf.subTitleSelectedFont textArray:weakSelf.subTitleArray indexpath:indexPath];
weakSelf.indicatorView.center = CGPointMake(cell.center.x - subWidth/2, weakSelf.frame.size.height - weakSelf.indicatorHeight);
}
else
{
weakSelf.indicatorView.center = CGPointMake(cell.center.x, weakSelf.frame.size.height - weakSelf.indicatorHeight);
}
}];
}
}
#pragma mark -- lazy
- (UICollectionView *)collectionView {
if(_collectionView == nil)
{
self.flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.flowLayout];
[_collectionView registerClass:[OCSegmentTitleCCell class] forCellWithReuseIdentifier:@"collectionCell"];
_collectionView.showsVerticalScrollIndicator = NO;
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.showsHorizontalScrollIndicator = NO;
_collectionView.backgroundColor = [UIColor clearColor];
}
return _collectionView;
}
- (UICollectionViewFlowLayout *)flowLayout {
if (_flowLayout == nil)
{
_flowLayout = [[UICollectionViewFlowLayout alloc] init] ;
}
return _flowLayout;
}
- (UIView *)indicatorView {
if(_indicatorView == nil)
{
_indicatorView = [[UIView alloc] init];
_indicatorView.backgroundColor = [UIColor blackColor];
}
return _indicatorView;
}
@end
3.cell
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface OCSegmentTitleCCell : UICollectionViewCell
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *subTitleLabel;
@end
NS_ASSUME_NONNULL_END
#import "OCSegmentTitleCCell.h"
@interface OCSegmentTitleCCell ()
@property (nonatomic, strong) UIView *labelView;
@end
@implementation OCSegmentTitleCCell
- (instancetype)initWithFrame:(CGRect)frame {
if(self = [super initWithFrame:frame])
{
[self makeConstraints];
}
return self;
}
#pragma mark -- 布局
- (void)makeConstraints {
[self.contentView addSubview:self.labelView];
[self.labelView addSubview:self.titleLabel];
[self.labelView addSubview:self.subTitleLabel];
[self.labelView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.contentView);
make.left.right.equalTo(self.contentView).offset(0);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.bottom.equalTo(self.labelView).offset(0);
}];
[self.subTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.top.bottom.equalTo(self.labelView).offset(0);
make.left.equalTo(self.titleLabel.mas_right).offset(0);
}];
}
#pragma mark -- lazy
- (UIView *)labelView {
if(_labelView == nil)
{
_labelView = [[UIView alloc] init];
}
return _labelView;
}
- (UILabel *)titleLabel {
if(_titleLabel == nil)
{
_titleLabel = [[UILabel alloc] init];
}
return _titleLabel;
}
- (UILabel *)subTitleLabel {
if(_subTitleLabel == nil)
{
_subTitleLabel = [[UILabel alloc] init];
}
return _subTitleLabel;
}
@end