分段控制器

16 篇文章 0 订阅

写在前面

项目中经常用到分段控制器,网上找的几个用着不是太顺手,细读了两份源码,其实也不难,二来各个项目特色不一样,觉得造个轮子还是有些必要的。

沙场点兵

所谓的分段控制器无非是顶部选择器跟下面的视图控制器联动。
那么怎么实现这种联动呢。
顶部的选择器可以用collectionView实现,有一下几点好处

  • 选中某一个通过代理方法可以方便的执行一些操作

  • 把当前选中的item居中动画,直接通过collectionView自带的方法实现

底部视图控制器可以用一个自定义containerViewController实现,内部添加对应的childViewController

  • 我们让containerViewController跟它的childViewController建立关系,管理childViewController的大小和位置,方便子控制器之间的切换

  • 将childViewController添加到容器后,容器会自动将相关的消息转发给childViewController,彼此又相互独立,子控制器管理自己的事务。

自定义containerViewController官方文档

Talk is cheap Show me your code

//  SDCursorView.h
#import <UIKit/UIKit.h>

@protocol SDCursorViewDelegate <NSObject>

- (void)sdCursorViewDelegateSelectIndex:(NSInteger )index;

@end


@interface SDCursorView : UIView

@property (nonatomic, weak)id <SDCursorViewDelegate> delegate;


/**
 *  标题和显示的页面,数量必须相等。
 */
@property (nonatomic, copy) NSArray *titles;
/**
 *  所要添加的viewController的实例。绘制界面时,会将viewController的view添加在scrollView上
 */
@property (nonatomic, copy) NSArray *controllers;

/**
 *  底部标示线颜色,默认 redColor
 */
@property (nonatomic, strong) UIColor *indicatorColor;

/**
 *  当前页面所在的controller,必须赋值
 */
@property (nonatomic, strong) UIViewController *parentViewController;

/**
 *  底部切换页面的容器高度
 */
@property (nonatomic, assign) CGFloat contentViewHeight;


/**
 *  默认都是14号字体
 */
@property (nonatomic, strong) UIFont *selectedFont;
@property (nonatomic, strong) UIFont *normalFont;

/**
 *  默认分别是 [UIColor redColor],[UIColor whiteColor]
 */
@property (nonatomic, strong) UIColor *selectedColor;
@property (nonatomic, strong) UIColor *normalColor;

/**
 *  当前选中的index。可以设置当前的index
 */
@property (nonatomic, assign) NSInteger currentIndex;

/**
 *  分割线位置调整。总是居中显示  默认(0,3,2,3)
 分割线默认高度为3, left可调整宽度,top可调整高度,bottom可调整lineView的y值
 */
@property (nonatomic, assign) UIEdgeInsets lineEdgeInsets;

/**
 *  调用此方法来绘制界面,required
 */
-(void)reloadPages;


@end


//  SDCursorView.m
#import "SDCursorView.h"
#import "SDSelectorCell.h"

@interface SDCursorView ()<UICollectionViewDelegateFlowLayout,UICollectionViewDataSource>

@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UICollectionViewFlowLayout *layout;
@property (nonatomic, strong) UIScrollView *rootScrollView;
@property (nonatomic, strong) UIView *lineView;


@end

static NSString *const cellIdentifier = @"selectorCell";

@implementation SDCursorView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor blackColor];
        _normalFont = _selectedFont = [UIFont systemFontOfSize:14];
        _normalColor = [UIColor whiteColor];
        _selectedColor = [UIColor redColor];
        _indicatorColor = [UIColor redColor];
        _currentIndex = 0;
        _lineEdgeInsets = UIEdgeInsetsMake(CGRectGetHeight(frame)-3, 0, 0, 0);
        [self addSubview:self.collectionView];
        [self.collectionView addSubview:self.lineView];
    }
    return self;
}

/**
 *  主动设置cursor取消选中item
 *
 *  @param index index
 */
- (void)deselectItemAtIndex:(NSInteger)index {
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
    [self.collectionView deselectItemAtIndexPath:indexPath animated:NO];
    SDSelectorCell *cell = (SDSelectorCell*)[self.collectionView cellForItemAtIndexPath:indexPath];
    cell.selected = NO;
}

/**
 *  主动设置cursor选中item
 *
 *  @param index index
 */
- (void)selectItemAtIndex:(NSInteger)index {
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:_currentIndex inSection:0];
    [self.collectionView selectItemAtIndexPath:indexPath
                                      animated:YES
                                scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];

    SDSelectorCell *cell = (SDSelectorCell*)[self.collectionView cellForItemAtIndexPath:indexPath];
    cell.selected = YES;
    CGRect rect = cell.frame;
    if (!cell) {
        UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
        rect = attributes.frame;
    }
    [self resizeLineViewWihtCellFrame:rect animated:YES];

    [self addChildViewController];


}

/**
 *  设置标识线的frame
 *
 *  @param frame cellFrame
 */
- (void)resizeLineViewWihtCellFrame:(CGRect)frame animated:(BOOL)animated {
    CGRect rect = CGRectMake(CGRectGetMinX(frame)+_lineEdgeInsets.left,
                             _lineEdgeInsets.top,
                             CGRectGetWidth(frame)-_lineEdgeInsets.left-_lineEdgeInsets.right,
                             CGRectGetHeight(self.collectionView.frame)-_lineEdgeInsets.top-_lineEdgeInsets.bottom);

    if (animated) {
        [UIView animateWithDuration:0.2f animations:^{
            self.lineView.frame = rect;
        }];
    }else{
        self.lineView.frame = rect;
    }
}

/**
 *  设置collectionView的偏移量,使得选中的项目居中
 *
 *  @param frame cellFrame
 */
- (void)setContentOffsetWithCellFrame:(CGRect)frame {
    CGFloat width = CGRectGetWidth(self.collectionView.frame)/2;
    CGFloat contentW = self.collectionView.contentSize.width;
    CGFloat offsetX = 0;

    if (CGRectGetMidX(frame) <= width || contentW <= width*2) {//当前选中的cell位于偏左,没有移动空间 || frame没有填满

        offsetX = 0;

    }else if (CGRectGetMidX(frame) + width >= contentW) {//当前选中cell偏右,没有移动空间

        offsetX = contentW - width*2;

    }else{//当前选中cell有左右移动空间
        offsetX = CGRectGetMidX(frame)-width;
    }

    [self.collectionView setContentOffset:CGPointMake(offsetX, 0) animated:YES];
}

/**
 *  将子viewController添加到scrollView上
 */
- (void)addChildViewController {
    //viewController
    UIViewController *controller = _controllers[_currentIndex];

    CGFloat startX = CGRectGetWidth(self.rootScrollView.bounds)*_currentIndex;
    if (!controller.parentViewController) {
        [self.parentViewController addChildViewController:controller];
        CGRect rect = self.rootScrollView.bounds;
        rect.origin.x = startX;
        controller.view.frame = rect;
        [self.rootScrollView addSubview:controller.view];
        [controller didMoveToParentViewController:self.parentViewController];
    }

    [self.rootScrollView setContentOffset:CGPointMake(startX, 0) animated:NO];

    if (self.delegate && [self.delegate respondsToSelector:@selector(sdCursorViewDelegateSelectIndex:)]) {
        [self.delegate sdCursorViewDelegateSelectIndex:_currentIndex];
    }
}


- (void)reloadPages {
    NSAssert(_titles.count == _controllers.count, @"titles' count is not equal to controllerNames' count");
    [self.collectionView reloadData];

    [self addChildViewController];
}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if ([self.rootScrollView isEqual:scrollView]) {
        CGFloat offsetX = scrollView.contentOffset.x;
        if (offsetX >= 0) {
            NSInteger index = offsetX / CGRectGetWidth(self.bounds);
            if (self.currentIndex != index) {
                self.currentIndex = index;
                [self selectItemAtIndex:self.currentIndex];

            }
        }
    }
}


#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return _titles.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    SDSelectorCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

    NSDictionary *dic = _titles[indexPath.item];

    [cell.button setImage:[UIImage imageNamed:dic[@"nomal"]] forState:UIControlStateNormal];
    [cell.button setImage:[UIImage imageNamed:dic[@"nomal"]] forState:UIControlStateNormal|UIControlStateHighlighted];
    [cell.button setImage:[UIImage imageNamed:dic[@"select"]] forState:UIControlStateSelected];
    [cell.button setImage:[UIImage imageNamed:dic[@"select"]] forState:UIControlStateSelected|UIControlStateHighlighted];

    if (indexPath.row == 0) {
        cell.shuline.hidden = YES;
    }else {
        cell.shuline.hidden = NO;
    }


    if (indexPath.item == _currentIndex) {
        [collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone];
        cell.selected = YES;
        [self resizeLineViewWihtCellFrame:cell.frame animated:NO];
    }

    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    if (_currentIndex == indexPath.item) {
        return;
    }
    self.currentIndex = indexPath.item;
    [self selectItemAtIndex:indexPath.item];

}

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
    SDSelectorCell *cell = (SDSelectorCell*)[collectionView cellForItemAtIndexPath:indexPath];
    cell.selected = NO;
}

#pragma mark - UICollectionViewDelegateFlowLayout

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat viewWidth = SCREEN_WIDTH - 28 - 7;
    CGFloat viewHeight = 77 - 7;

    return CGSizeMake(viewWidth/4, viewHeight);
}


#pragma mark - setter&getter

- (void)setIndicatorColor:(UIColor *)indicatorColor {
    _indicatorColor = indicatorColor;
    self.lineView.backgroundColor = indicatorColor;
}

- (void)setTitles:(NSArray *)titles {
    _titles = titles;
    self.rootScrollView.contentSize = CGSizeMake(CGRectGetWidth(self.bounds)*self.titles.count, 100);
}

- (UIScrollView*)rootScrollView {
    if (!_rootScrollView) {
        _rootScrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, CGRectGetHeight(self.bounds) + 7 + NavigationBarHeight, CGRectGetWidth(self.bounds), self.contentViewHeight)];
        _rootScrollView.backgroundColor = [UIColor whiteColor];
        _rootScrollView.pagingEnabled = YES;
        _rootScrollView.delegate = self;
        _rootScrollView.alwaysBounceHorizontal = YES;
        _rootScrollView.showsVerticalScrollIndicator = NO;
        _rootScrollView.showsHorizontalScrollIndicator = NO;
        _rootScrollView.scrollsToTop = NO;
        _rootScrollView.bounces = YES;
        [self.parentViewController.view addSubview:_rootScrollView];

        NSAssert(self.parentViewController, @"self.parentViewController must has a value");
    }
    return _rootScrollView;
}

- (UIView*)lineView {
    if (!_lineView) {
        _lineView = [[UIView alloc]init];
        _lineView.backgroundColor = _indicatorColor;
    }
    return _lineView;
}

- (UICollectionView*)collectionView {
    if (!_collectionView) {
        _layout = [[UICollectionViewFlowLayout alloc]init];
        _layout.minimumLineSpacing = 0;
        _layout.minimumInteritemSpacing = 0;
        _layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(17.5, 0, SCREEN_WIDTH - 35, 70) collectionViewLayout:_layout];
        _collectionView.showsVerticalScrollIndicator = NO;
        _collectionView.showsHorizontalScrollIndicator = NO;
        _collectionView.scrollsToTop = NO;
        _collectionView.backgroundColor = [UIColor clearColor];
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
        [_collectionView registerClass:[SDSelectorCell class]
            forCellWithReuseIdentifier:cellIdentifier];
    }
    return _collectionView;
}

@end
//  SDSelectorCell.h
#import <UIKit/UIKit.h>

@interface SDSelectorCell : UICollectionViewCell

@property (nonatomic, strong)UIButton *button;

@property (nonatomic, strong) UIView *shuline;

@end

//  SDSelectorCell.m
#import "SDSelectorCell.h"

@implementation SDSelectorCell

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {

        [self.contentView addSubview:self.button];
        _shuline = [[UIView alloc] init];
        _shuline.layer.backgroundColor = HEXCOLOR(0xF2F2F2).CGColor;

        [self.contentView addSubview:_shuline];

    }
    return self;
}


- (void)setSelected:(BOOL)selected {
    super.selected = selected;

    self.button.selected = selected;
}


- (UIButton *)button {
    if (!_button) {
        _button = [UIButton buttonWithType:UIButtonTypeCustom];
        _button.userInteractionEnabled = NO;
        [_button setBackgroundImage:[BaseMethod createImageWithColor:[UIColor whiteColor]] forState:UIControlStateNormal];
        [_button setBackgroundImage:[BaseMethod createImageWithColor:[UIColor yellowColor]] forState:UIControlStateNormal|UIControlStateHighlighted];
        [_button setBackgroundImage:[BaseMethod createImageWithColor:HEXCOLOR(0xd9effe)] forState:UIControlStateSelected];
        [_button setBackgroundImage:[BaseMethod createImageWithColor:[UIColor blueColor]] forState:UIControlStateSelected|UIControlStateHighlighted];
    }
    return _button;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    _shuline.frame = CGRectMake(0, 8, 1, 52);

    _button.frame = self.bounds;
}

@end

在外部调用

NSDictionary *dic3 = [NSDictionary dictionaryWithObjectsAndKeys:@"home_top_nomal_1",@"nomal",@"home_top_select_1",@"select", nil];
NSDictionary *dic2 = [NSDictionary dictionaryWithObjectsAndKeys:@"home_top_nomal_2",@"nomal",@"home_top_select_2",@"select", nil];
NSDictionary *dic4 = [NSDictionary dictionaryWithObjectsAndKeys:@"home_top_nomal_3",@"nomal",@"home_top_select_3",@"select", nil];
NSDictionary *dic1 = [NSDictionary dictionaryWithObjectsAndKeys:@"home_top_nomal_4",@"nomal",@"home_top_select_4",@"select", nil];

UIViewController *controller1 = [[ExampleBaseViewController alloc] initWithTitle:@"1" viewClass:nil];
UIViewController *controller2 = [[ExampleBaseViewController alloc] initWithTitle:@"2" viewClass:nil];
UIViewController *controller3 = [[ExampleBaseViewController alloc] initWithTitle:@"3" viewClass:nil];
UIViewController *controller4 = [[ExampleBaseViewController alloc] initWithTitle:@"4" viewClass:nil];

_cursorView.titles = @[dic1, dic2,dic3];
_cursorView.controllers = @[controller1,controller2,controller3];
_cursorView.currentIndex = 0;
[_cursorView reloadPages];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值