我在前一篇博客中《iOS开发实战——CollectionView点击事件与键盘隐藏结合案例》详细实现了CollectionView与键盘组合操作中出现的多种情况,并解决了交互体验上的一些问题。在实际项目中也的确可以采用这种方法来操作。但是问题来了,原来的界面我们是使用UIView来操作的,也就是界面是不可滚动的。然而更为常见的场景是一个ScrollView,界面可以进行上下滚动。所以,这篇博客主要是对前一个案例进行优化。还有一个问题是,在自动布局Masonry结合ScrollView中,会碰到一些坑和技巧,也是我们主要解决的问题。项目代码上传至 https://github.com/chenyufeng1991/ShowHiddenKeyboard 。
今天要实现的效果如下:
。
由于大部分功能和之前的类似,我把重点放在ScrollView和Masonry的结合上。
(1)UI布局的思路是:最外面是一个ScrollView,然后里面放一个取名为contentView的UIView,作为放置其他元素的容器。contentView的高度会随着内部元素的总高度的变化而变化,也就是ScrollView需要滚动的总高度。
// 最外部的scrollView self.scrollView = [[UIScrollView alloc] init]; self.scrollView.backgroundColor = [UIColor orangeColor]; self.scrollView.scrollEnabled = YES; self.scrollView.userInteractionEnabled = YES; self.scrollView.bounces = NO; self.scrollView.delegate = self; self.automaticallyAdjustsScrollViewInsets = NO; [self.view addSubview:self.scrollView]; [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(weakSelf.view).offset(64); make.left.equalTo(weakSelf.view); make.right.equalTo(weakSelf.view); make.bottom.equalTo(weakSelf.view); }]; // 包含所有子View的容器 self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 64, [[UIScreen mainScreen] bounds].size.width, 200)]; self.contentView.backgroundColor = [UIColor redColor]; [self.scrollView addSubview:self.contentView]; [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(weakSelf.scrollView); make.width.equalTo(weakSelf.scrollView); }];
这里设定ScrollView铺满全屏。而里面的contentView也是铺满整个ScrollView,并且一定要显式的设置宽度等于scrollView的宽度,也就是水平不可滑动。
在设置scrollView时建议加上self.automaticallyAdjustsScrollViewInsets = NO。这个参数比较坑爹,默认是YES,也就是默认在你使用scrollView的时候给你加了内边距,而这个往往是我们不需要的。如果默认置为YES的话,发现UI怎么都调不出我们想要的效果,往往会在顶部一部分。【ps:其实在这里你也可以考虑到是导航栏的原因,看你个人需求是ScrollView是否要到达屏幕顶部还是只到达导航栏底部。】
(2)由于我要让界面有滑动的效果,所以要让ScrollView里面的contentSize大于height,所以我在下面铺了一张很长的图片来模拟。
self.bottomImageView = [[UIImageView alloc] init]; self.bottomImageView.image = [UIImage imageNamed:@"bottom"]; [self.contentView addSubview:self.bottomImageView]; [self.bottomImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(weakSelf.collectionView.mas_bottom).offset(20); make.left.equalTo(weakSelf.contentView); make.right.equalTo(weakSelf.contentView); make.height.equalTo(@500); }];
(3)其实自动布局写到这里的时候,所有的设置都已经完成了。但是一运行代码,发现并不是这样的。ScrollView根本就不能滚动。原来想的是:contentView由于add了很多的元素,所以contentView的高度就应该是这些元素实际布局中的总和。Masonry理应有这种自动计算的功能,但事实却不是这样的。调试上面的代码会发现contentView的高度居然是0. 查看约束设置代码,我们的确没有显式的设置高度。很遗憾,Masonry没有自动完成这个功能。所以需要在上面的子元素约束完成以后,实现如下代码:
// 这里要重新设置容器的高度,其实只要固定底部即可。 [self.contentView mas_updateConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(self.bottomImageView); }];
更新contentView容器的约束。也就是添加了底部约束到那张长长的图片的底部,这样也就相当于设置了高度。这样ScrollView就能正常滚动了。
(4)对于CollectionView的处理同前一个案例一样,这里不再赘述。由于添加了ScrollView,所以原先touchesEnded方法就不会被调用了,因为手势操作首先会去触发ScrollView中的操作。所以我们就要来实现ScrollViewDelegate中的方法来实现键盘隐藏。
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self hideKeyboard]; }
当ScrollView开始滚动的时候隐藏键盘。由于此时的背景空白区域是contentView,所以添加一个tap手势:
UITapGestureRecognizer *tapView = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)]; [self.contentView addGestureRecognizer:tapView];
经过以上实现后,就能实现ScrollView、CollectionView和键盘之间的事件处理了,也就是上面Gif中的动效。其实实现的关键就是上面ScrollView和Masonry的结合上。