背景
公司一个项目UI改版,首页有个banner,他的pageControl跟我们平常见到的不太相像。大概长这样:
IMG_0737.PNG
思路
原先我是打算直接修改选中的样式。但是发现它只提供了修改默认颜色和选中颜色两个属性。但是由于oc是一门动态性的语言,我们可以通过找到这个被选中的原点的视图,然后使用KVC的方式修改它。
方法参考:iOS 修改UIPageControl样式
但是这并不能实现我想要的效果。因为..
结论
pageControl实际上就是一组小圆点+选中图形与banner图片的联动。所以我们可以自己画一个分页控制器。
开始
- 创建圆点数组。
圆点的数量是根据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是圆点父视图,用来限制圆点散列范围。使用这种方法能方便的对一组视图进行等间距布局。
- 创建选中图形。
画一个符合设计稿尺寸的view就可以。不过有一点需要注意,由于我们在初始化的时候就创建了它。但是圆点是在pageCount的set方法里再创建添加的,所以我们需要在圆点创建完成后在调用一遍
[_dotBgView addSubview:_selectView];
这样才能保证选中视图一直覆盖住圆点。
-
选中视图与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的时候触发手势事件。
其中有两点需要注意:
- 只能点击image所在区域才能传递手势事件;
- 需要传递被点击的image所在的下标。
有两种方式可以解决:
- 拿到scroll视图内所有点击和不可点击的locationX坐标范围。根据拿到的点击位置,去判断当前位置是否可点击,以及可点击位置所在的下标index。
由于图片张数量是可变的,为了拿到这个可点击+不可点击 范围,我们会用到循环
scrollLeftPadding + (imageInset+ImageWidth)*i
如果在tap的落点在这个范围内,说明是可点击的,循环因子【i】就是他的下标。
在图片较多的情况下这种方式不可取。 - 将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~
网友评论