iOS banner+自定义pageControl

背景

公司一个项目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~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359