预览
ScrollView.gif
前言
好奇心日报的有的文章页面是这样展示的,点击其中一个就可以进入单个的文章详情页面,于是想了一下该怎么实现。
思路
一开始想的是用一个ScrollView去实现,后来发现一个ScrollView实现的话最后一页会有问题,也没有想到解决方案,于是就用两个ScrollView来实现
屏幕快照 2017-02-21 下午3.32.44.png
屏幕快照 2017-02-21 下午3.32.55.png
第一张图蓝色标记的是底下的小的ScrollView,它的作用是实现翻页效果,第二张图蓝色标记的是上层的大的ScrollView,用来展示内容,用小的UIScrollView来带动大的ScrollView滑动,大的ScrollView不参与用户滑动事件
实现
假设展示4页内容
一些宏定义
#define kScreenWidth ([UIScreen mainScreen].bounds.size.width)
#define kScreenHeight ([UIScreen mainScreen].bounds.size.height)
#define kContentWidth 250
#define kContentHeight 400
#define kPadding 20
SmallScrollView
需要注意的是,小的ScrollView最后一页的宽度需要计算一下
self.smallScrollView = ({
UIScrollView *scroll = [[UIScrollView alloc] init];
scroll.delegate = self;
scroll.showsHorizontalScrollIndicator = NO;
scroll.pagingEnabled = YES;
[self addSubview:scroll];
[scroll makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self).offset(UIEdgeInsetsMake(0, kPadding / 2, 0, kScreenWidth - kPadding - kContentWidth - kPadding / 2));
}];
UIView *content = [[UIView alloc] init];
[scroll addSubview:content];
[content makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scroll);
make.height.equalTo(scroll);
}];
UIView *lastView;
for (NSInteger i=0; i<4; i++) {
UIView *view = [[UIView alloc] init];
[content addSubview:view];
if (i == 0) {
[view makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(kPadding / 2);
make.top.bottom.equalTo(content);
make.width.equalTo(kContentWidth);
make.height.equalTo(kContentHeight);
}];
} else if (i == 3) {
[view makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(lastView.right).offset(kPadding);
make.top.bottom.equalTo(content);
make.width.equalTo(kContentWidth - (kScreenWidth - kPadding * 2 - kContentWidth));
make.height.equalTo(kContentHeight);
}];
} else {
[view makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(lastView.right).offset(kPadding);
make.top.bottom.equalTo(content);
make.width.equalTo(kContentWidth);
make.height.equalTo(kContentHeight);
}];
}
lastView = view;
}
[content makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(lastView.right).offset(kPadding / 2);
}];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(smallScrollTap:)];
[scroll addGestureRecognizer:tap];
scroll;
});
BigScrollView
self.bigScrollView = ({
UIScrollView *scroll = [[UIScrollView alloc] init];
scroll.clipsToBounds = NO;
scroll.showsHorizontalScrollIndicator = NO;
[self addSubview:scroll];
[scroll makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self).offset(UIEdgeInsetsMake(0, kPadding / 2, 0, kPadding / 2));
}];
UIView *content = [[UIView alloc] init];
[scroll addSubview:content];
[content makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scroll);
make.height.equalTo(scroll);
}];
UIView *lastView;
for (NSInteger i=0; i<4; i++) {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [self randomColor];
[content addSubview:view];
if (i == 0) {
[view makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(kPadding / 2);
make.top.equalTo(content);
make.width.equalTo(kContentWidth);
make.height.equalTo(kContentHeight);
}];
} else {
[view makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(lastView.right).offset(kPadding);
make.top.equalTo(content);
make.width.equalTo(kContentWidth);
make.height.equalTo(kContentHeight);
}];
}
lastView = view;
// UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
// [btn setBackgroundColor:[UIColor lightGrayColor]];
// [btn setTitle:@"Button" forState:UIControlStateNormal];
// [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
// [view addSubview:btn];
// [btn makeConstraints:^(MASConstraintMaker *make) {
// make.centerX.equalTo(view);
// make.width.equalTo(100);
// make.height.equalTo(50);
// make.bottom.equalTo(-50);
// }];
}
[content makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(lastView.right).offset(kPadding / 2);
}];
scroll;
});
其它
前面说到大的ScrollView不参与用户事件,那该怎样做到呢?
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return self.smallScrollView;
}
就是这么简单。。。所有事件都让底下的小的ScrollView去处理,实现ScrollView的代理方法,进而带动大的ScrollView滑动
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.smallScrollView) {
[self.bigScrollView setContentOffset:scrollView.contentOffset animated:NO];
}
}
关于滑动的就处理完了
点击事件
下面就是处理用户点击单个页面了,给小的ScrollView添加单击手势,然后将点转化到大的ScrollView即完成了单击手势的检测,假如页面上有button,判断点是否在button内进而处理
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(smallScrollTap:)];
[scroll addGestureRecognizer:tap];
- (void)smallScrollTap:(UITapGestureRecognizer *)sender{
[self.bigSubViews removeAllObjects];
[self listSubviewsOfView:self.bigScrollView];
CGPoint bigPoint = [sender locationInView:self.bigScrollView];
for (UIView *view in self.bigSubViews.reverseObjectEnumerator) {
if ([view isKindOfClass:[UIButton class]]) {
CGPoint buttonPoint = [view convertPoint:bigPoint fromView:self.bigScrollView];
if (CGRectContainsPoint(view.bounds, buttonPoint)) {
[(UIButton *)view sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
}
}
- (void)listSubviewsOfView:(UIView *)view {
NSArray *subviews = [view subviews];
if ([subviews count] == 0) return;
for (UIView *subview in subviews) {
[self.bigSubViews addObject:subview];
[self listSubviewsOfView:subview];
}
}
不足
我也不清楚别人是怎么实现这种ScrollView效果的,这个方法我也不清楚是不是最好的,只是我能想到的方法,当然有些许不足,比如都交给小的ScrollView处理,展示的内容如果有button,button在normall,highlighted,selected状态设置了不同的样式,该实现是没有办法展示的,因为我只是发送了一个事件过去。所以如果你有更好的方法,不妨发表一下!