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

分段控制器

程序员文章站 2024-03-09 18:01:47
...

写在前面

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

沙场点兵

所谓的分段控制器无非是顶部选择器跟下面的视图控制器联动。
那么怎么实现这种联动呢。
顶部的选择器可以用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];
相关标签: 分段控制器