手把手教你封装属于自己的分段滚动视图(下)

接下来是segmentView

这个黑色可部分是collectionView,因为他自由度比较高,简单易用~

segmentView将item和itemContent结合在一起:

 

以下是初始化以及布局:

 

///接口部分:

@interface YCSegmentView : UIView

 

///非选中颜色

@property (nonatomic,strong) UIColor *normalColor;

///选中颜色

@property (nonatomic,strong) UIColor *highlightColor;

///字体

@property (nonatomic,strong) UIFont  *font;

 

(instancetype)initWithFrame:(CGRect)frame titleHeight:(CGFloat)height viewControllers:(NSArray  *)viewControllers;

 

@end

 

@implementation YCSegmentView

……

 

(instancetype)initWithFrame:(CGRect)frame titleHeight:(CGFloat)height viewControllers:(NSArray *)viewControllers {

 

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

        _titleHeight = height;

        _viewControllers = viewControllers;

        [self setupAllViews];

    }

    return self;

}

 

/*

因为我们想让用户更加方便使用,

所以希望用户只需要传入视图控制器,

就可以完成控件的初始化,

控制器有`title`属性。

我们就可以直接取到控制器的标题来初始化`itemContent`

*/

 

(void)setupAllViews {

    NSMutableArray *titles = [NSMutableArray arrayWithCapacity:_viewControllers.count];

    for (UIViewController *vc in _viewControllers) {

        [titles addObject:vc.title];

    }

 

    self.titleView = [[YCSegmentItemsContentView alloc] initWithFrame:CGRectZero titles:titles];

    self.titleView.delegate = self;

    [self addSubview:self.titleView];

 

    self.cLayout = [[UICollectionViewFlowLayout alloc] init];

 

///设置collectionView的滚动方式为水平滚动

    self.cLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

    self.collectionView = [[UICollectionView alloc] initWithFrame:(CGRectZero) collectionViewLayout:self.cLayout];

 

///这是collectionView按页滚动

    self.collectionView.pagingEnabled = YES;

    self.collectionView.delegate = self;

    self.collectionView.dataSource = self;

 

///注意!这里为`collectionView`注册`cell` (YCSegmentViewUnit),下文提到。

    [self.collectionView registerClass:[YCSegmentViewUnit class] forCellWithReuseIdentifier:@"YCSegmentViewUnit"];

 

///注意!这里为`collectionView`添加观察者,观察属性`contentOffset`的变化,来获得页数,控制`itemContent`选择哪一个`item`

    [self.collectionView addObserver:self forKeyPath:@"contentOffset" options:(NSKeyValueObservingOptionNew) context:nil];

    [self addSubview:self.collectionView];

}

 

(void)layoutSubviews {

    self.titleView.frame = CGRectMake(0, 0, self.frame.size.width, _titleHeight);

    self.collectionView.frame = CGRectMake(0, _titleHeight, self.frame.size.width, self.frame.size.height - _titleHeight);

}

 

……

@end

 

还有重写KVO发现属性变化后会调用的方法:

 

……

(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

 

    CGPoint offset = self.collectionView.contentOffset;

///这里` + 0.5` 是为了当滚动到页面一半的时候,就已经算作下一页或者上一页

    CGFloat pageFloat = offset.x / self.collectionView.bounds.size.width + 0.5;

    if (pageFloat  _viewControllers.count) {

        pageFloat = _viewControllers.count;

    }

    NSInteger page = (NSInteger)pageFloat;

    self.titleView.page = page;

}

……

 

在类的延展中有这样一些属性和变量:

 

@interface YCSegmentView ()

{

    NSArray *_viewControllers;//用来保存所有的控制器

    CGFloat  _titleHeight;//用来保存`itemContent`的高度

}

@property (nonatomic,strong) UICollectionViewFlowLayout *cLayout;

@property (nonatomic,strong) UICollectionView *collectionView;

@property (nonatomic,strong) YCSegmentItemsContentView *titleView;

 

@end

 

剩下的就是实现collectionView的协议方法,以及itemContent的协议方法:

 

(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {

    return _viewControllers.count;

}

 

(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {

    return 1;

}

 

(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    YCSegmentViewUnit * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"YCSegmentViewUnit" forIndexPath:indexPath];

    UIViewController *vc = _viewControllers[indexPath.section];

    if (!vc.isViewLoaded) {

        [vc loadViewIfNeeded];

    }

 

    cell.view = vc.view;

 

    return cell;

}

 

(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    return collectionView.bounds.size;

}

 

(void)didSelectedButtonAtIndex:(NSInteger)index {

    CGFloat width = self.collectionView.bounds.size.width;

    [self.collectionView setContentOffset:(CGPointMake(width * index, 0)) animated:YES];

}

 

接下来我们看一下YCSegmentViewUnit类的代码:

 

@interface YCSegmentViewUnit : UICollectionViewCell

 

@property (nonatomic,strong) UIView *view;

 

@end

 

@implementation YCSegmentViewUnit

 

(void)setView:(UIView *)view {

    if (_view) {

        [_view removeFromSuperview];

    }

    [self.contentView addSubview:view];

    [self setNeedsLayout];

    _view = view;

}

 

(void)layoutSubviews {

    self.view.frame = self.contentView.bounds;

}

 

@end

 

当我们为cell的view属性赋值的时候,需要考虑当前的_view是否存在,如果存在就从contentView上移除,然后将新传入的view添加到contentView上,然后设置view的位置和大小贴合contentView。

最后千万不要忘了,_view = view,因为我们需要实力变量保存当前的view!

 

这样这个控件的封装就搞定啦~

 

不!还有非常重要的一点!

我们使用了KVO对collectionView进行了属性观察,但是如果观察着被释放了,肯定会出现问题,所以我们需要在YCSegmentView中重写- (void)dealloc方法,在dealloc中移除对collectionView的观察:

 

(void)dealloc {

    [self.collectionView removeObserver:self forKeyPath:@"contentOffset"];

}

 

恩,这样子就大功告成了,我们实例化一个看一看~~

 

@implementation ViewController

 

(void)viewDidLoad {

    [super viewDidLoad];

 

    HomeViewController *vc1 = [[HomeViewController alloc] init];

    vc1.title = @"首页";

 

    RankViewController *vc2 = [[RankViewController alloc] init];

    vc2.title = @"榜单";

 

    MasterViewController *vc3 = [[MasterViewController alloc] init];

    vc3.title = @"达人";

 

    ChoicenessViewController *vc4 = [[ChoicenessViewController alloc] init];

    vc4.title = @"每周精选";

 

 

    YCSegmentView *view = [[YCSegmentView alloc] initWithFrame:CGRectMake(0, 20, self.view.bounds.size.width, self.view.bounds.size.height) titleHeight:44 viewControllers:@[vc1, vc2, vc3, vc4]];

    view.highlightColor = [UIColor orangeColor];

    [self.view addSubview:view];

}

 

 

没错,就是这个样子~

 

源码:https://github.com/ilakeYC/YCSegment

 

posted @ 2016-05-10 16:51  FMDN  阅读(264)  评论(0编辑  收藏  举报