美文网首页
iOS banner+自定义pageControl

iOS banner+自定义pageControl

作者: 不明Xia落 | 来源:发表于2019-07-12 18:05 被阅读0次

    背景

    公司一个项目UI改版,首页有个banner,他的pageControl跟我们平常见到的不太相像。大概长这样:


    IMG_0737.PNG

    思路

    原先我是打算直接修改选中的样式。但是发现它只提供了修改默认颜色和选中颜色两个属性。但是由于oc是一门动态性的语言,我们可以通过找到这个被选中的原点的视图,然后使用KVC的方式修改它。
    方法参考:iOS 修改UIPageControl样式
    但是这并不能实现我想要的效果。因为..

    未切换情况下.PNG 切换之后.PNG

    结论

    pageControl实际上就是一组小圆点+选中图形与banner图片的联动。所以我们可以自己画一个分页控制器。

    开始

    1. 创建圆点数组。
      圆点的数量是根据banner图片的数量来的,所以我们需要在.h文件中开放一个设置变量pageCount,在.m文件中重写set方法画圆点。
      题外话:圆点的约束。一直在用Masonry来做布局,但是发现自己对它的了解很片面。在前辈那里学到了使用它的等间距布局方法。
    - (void)setPageCount:(NSInteger)pageCount {
        if (!pageCount) {
            return;
        }
        __weak typeof(self) weakSelf = self;
        ...(省略)
        [_dotBgView mas_updateConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(@(40+pageCount*15-10));
        }];
        ...(省略)
        [_dotArray mas_distributeViewsAlongAxis:(MASAxisType)MASAxisTypeHorizontal withFixedItemLength:5 leadSpacing:20 tailSpacing:20];
       
        [_dotArray mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerY.equalTo(weakSelf.dotBgView);
            UIView *firstView = (UIView *)weakSelf.dotArray[0];
            make.height.mas_equalTo(firstView.mas_width);
    
        }];
        ...(省略)
    }
    

    dotArray是存放圆点的可变数组,dotBgView是圆点父视图,用来限制圆点散列范围。使用这种方法能方便的对一组视图进行等间距布局。

    1. 创建选中图形。
      画一个符合设计稿尺寸的view就可以。不过有一点需要注意,由于我们在初始化的时候就创建了它。但是圆点是在pageCount的set方法里再创建添加的,所以我们需要在圆点创建完成后在调用一遍
    [_dotBgView addSubview:_selectView];
    

    这样才能保证选中视图一直覆盖住圆点。

    1. 选中视图与banner图片的联动。
      这个联动关系有两种:

      3.1 pageControl内部的点击 -> bannerScroll的contentOffset改变
      给pageControl添加一个手势,根据手势的点击位置与当前选中视图的前后关系(注意边界情况,不能超过最左边和最右边。),来判断选中视图的移动。currentPage用来记录当前选中行。

    
    - (void)tapView:(UITapGestureRecognizer *)tap {
        __weak typeof(self) weakSelf = self;
        CGFloat locationX = _dotBgView.frame.origin.x + _selectView.frame.origin.x;
        CGPoint point = [tap locationInView:self];
        if (point.x > locationX) {
            if (_currentPage<_dotArray.count-1) {
                _currentPage+=1;
            }
        } else {
            if (_currentPage > 0) {
                _currentPage-=1;
            }
        }
        [UIView animateWithDuration:1.0 animations:^{
            [self->_selectView mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.size.mas_equalTo(CGSizeMake(12, 5));
                UIView *indexView = (UIView *)weakSelf.dotArray[weakSelf.currentPage];
                make.centerY.equalTo(indexView);
                make.centerX.equalTo(indexView);
            }];
        }];
        if (self.selectAction) {
            self.selectAction(_currentPage);
        }
    }
    

    selectAction是向banner传递消息的闭包,告诉banner当前选中了第几个圆点,需要展示哪张图片。

    _pageControl.selectAction = ^(NSInteger index) {
            [UIView animateWithDuration:0.8 animations:^{
                weakSlef.scrollView.contentOffset = CGPointMake((BannerImageWidth+10) * index, weakSlef.scrollView.contentOffset.y);
            }];
        };
    

    3.2 bannerScroll的滚动 -> pageControl的选中视图位置改变
    currentPage除了在pageControl内部记录当前选中行,还需要担当 banner传递消息给pageControl的帮手。所以currentPage也是BannerPageControl.h中外放的变量,然后在.m文件重写set方法,改变选中视图位置。

    - (void)setCurrentPage:(NSInteger)currentPage {
        __weak typeof(self) weakSelf = self;
        _currentPage = currentPage;
        [UIView animateWithDuration:1.0 animations:^{
            [weakSelf.selectView mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.size.mas_equalTo(CGSizeMake(12, 5));
                UIView *currentView = (UIView *)weakSelf.dotArray[currentPage];
                make.centerY.equalTo(currentView);
                make.centerX.equalTo(currentView);
            }];
        }];
    }
    

    banner相关-滑动滚动

    项目总用到的banner是很久之前从别的地方拷来的代码,但是不知道来源了。。之前的banner是一屏一屏的滚动。但是现有的项目是 在两侧能够看见上一张和下一张。重写了他的滚动逻辑。
    目前是:如果滚动超过图片的一半,则滚动到下一张。滚动未超过图片的一半,则滚动到未超过图片的上一张。

    部分代码
    NSInteger currentIndex;
    float indexF = endX / itemWidth;
    currentIndex = (NSInteger)(indexF+0.5);
    [UIView animateWithDuration:0.8 animations:^{
          scrollView.contentOffset = CGPointMake(currentIndex * itemWidth, scrollView.contentOffset.y);
    }];
    self.pageControl.currentPage = currentIndex;
    

    banner相关-点击事件

    我们给放置图片的父视图scrollView一个tap的手势。然后会在用户点击这个scroll的时候触发手势事件。
    其中有两点需要注意:

    1. 只能点击image所在区域才能传递手势事件;
    2. 需要传递被点击的image所在的下标。

    有两种方式可以解决:

    1. 拿到scroll视图内所有点击和不可点击的locationX坐标范围。根据拿到的点击位置,去判断当前位置是否可点击,以及可点击位置所在的下标index。
      由于图片张数量是可变的,为了拿到这个可点击+不可点击 范围,我们会用到循环
      scrollLeftPadding + (imageInset+ImageWidth)*i
      如果在tap的落点在这个范围内,说明是可点击的,循环因子【i】就是他的下标。
      在图片较多的情况下这种方式不可取。
    2. 将location的坐标转换为屏幕内可见的坐标。在0-ScreenWidth之间。我们可以通过减去scroll的偏移量去拿到它,下标就是
      偏移量/(image宽度+image间距)
      这样我们就能方便的去判断点击和不可点击,而且不用去循环,所以我们选择这种实现方式。代码如下
    - (void)selectIndexImage:(UITapGestureRecognizer *)tap {
        CGPoint location = [tap locationInView:_scrollView];
        CGFloat offSetX = location.x - _scrollView.contentOffset.x;
        if (offSetX<17.5 || offSetX>(BannerImageWidth-17.5)) {
            return;
        }
        NSLog(@"_scrollView.contentOffset.x=%2f",_scrollView.contentOffset.x);
        NSInteger index = _scrollView.contentOffset.x / (BannerImageWidth+10);
        if (self.clickWithBlock) {
            self.clickWithBlock(index);
        }
    }
    

    then

    如果能加个动画效果就完美了
    代码:https://github.com/bumingxialuo/BannerPageControl.git
    对你有用的话点个star~

    相关文章

      网友评论

          本文标题:iOS banner+自定义pageControl

          本文链接:https://www.haomeiwen.com/subject/cpjykctx.html