zoukankan      html  css  js  c++  java
  • 手把手教你封装属于自己的分段滚动视图(下)

    接下来是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

  • 相关阅读:
    HashMap源码分析
    LinkedList源码分析
    ArrayList源码学习
    Java容器知识总结
    Collections 工具类和 Arrays 工具类常见方法
    Java基础知识
    MySQL高级之索引优化分析
    MySQL命令大全
    Java IO
    SpringCloud笔记
  • 原文地址:https://www.cnblogs.com/fengmin/p/5478401.html
Copyright © 2011-2022 走看看