模仿高德地图搜索控件。
先上一下效果图吧:
项目里有需要用到这个功能,看起来很简单很清晰的功能,可是真正当自己写的时候,发现有很多需要注意的地方
需要注意的主要有两个地方:
一是手势与tableView的scroll滑动冲突的问题;
还有一个就是searchBar的键盘以及searchBar在Editing状态下的动画问题。
下面就主要说说这些问题
1. 手势与tableView的scroll滑动冲突
另外新建一个视图控制器来控制tableView的滑动,用轻扫手势UISwipeGestureRecognizer来控制这个滑动动作。通过swipe的方法可以判断手势是向上滑还是向下滑,然后再重新设置tableView的位置。想法是这样,然而当运行的时候发现做手势的时候只有table里面的scroll在滑动,swipe手势并没有响应。这就是我们要说的第一个问题:手势与tableView的scroll滑动冲突的问题。点进去看swipe的delegate,里面提供了好几种方法,其中这个方法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
其中 Simultaneously ,查了一下翻译为“与... 同步地”,整句就是手势同步响应的意思,那就是你了。测试发现,当返回值为NO时 swipe不响应手势 table响应手势;返回值为YES swipe、table都会响应手势。return YES swipe就会响应了。但是无论返回值为什么,table都会响应,而当需要滑动整个table的位置时 又不需要table里面的scroll滑动怎么办呢,没关系,table有一个scrollEnabled,把table的scrollEnabled为No就不会响应table了
/** 返回值为NO swipe不响应手势 table响应手势 返回值为YES swipe、table也会响应手势, 但是把table的scrollEnabled为No就不会响应table了 */ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { // NSLog(@"----------- table = %f ------------",self.vc.table.contentOffset.y); // 触摸事件,一响应 就把searchBar的键盘收起来 // searchBar收起键盘 UIButton *cancelBtn = [self.vc.searchController.searchBar valueForKey:@"cancelButton"]; //首先取出cancelBtn // 代码触发Button的点击事件 [cancelBtn sendActionsForControlEvents:UIControlEventTouchUpInside]; // 当table Enabled且offsetY不为0时,让swipe响应 if (self.vc.table.scrollEnabled == YES && self.vc.table.contentOffset.y != 0) { return NO; } if (self.vc.table.scrollEnabled == YES) { return YES; } return NO; }
2. searchBar的键盘以及searchBar在Editing状态下的动画问题
这里还有一个细节就是,收起键盘,不是用的一般常用的 endEditing:<#(BOOL)#>, 或resignFirstResponder ,因为searchBar封装的textField有点怪,当点击它状态变成编辑状态时,searchBar就跑出了它的父View,我感觉它是跑到了整个屏幕的最上面一层,所以使用上面说的两种方法收起键盘的时候,searchBar已经失去了控制,后面大部队再做动画、位移什么的,它就一直停留在自己原来的位置,比如这样的:
找了一天也没找到有效的办法,要是能像点击了取消按钮后自动收起键盘的效果就好了,
好在找到了这个方法:通过KVC获取到searchBar的私有属性cancelButton,然后通过代码给它发送了TouchUpInside的方法,一运行,还真的可以达到完美的收起键盘的效果。
还有一个问题就是刚刚提到的searchBar,以前我记得用的是UISearchBar和UISearchDisplayController一起用,不过UISearchDisplayController在ios8以后就被弃用了,就用了UISearchController ,而且它正好提供了UISearchBar、UITableView,而且内部提供了良好的封装,可以方便的实现搜索列表。 UISearchController的使用方法在网上一搜一大堆,人家的都讲得很细致,也没什么好说的,
这里就提供一个小tips:设置取消按钮为中文字:
1.
[_searchController.searchBar setValue:@"取消" forKey:@"_cancelButtonText"];// KVC访问私有属性
2.
在plist中设置语言Localization native development region 为“China”
这两种方法都亲测有效,而且简单方便,一秒钟就可以搞定,总比网上那些“继承searchBar,然后在里面重写它的方法”要好多了,我也是醉了
这里还是searchBar的问题,需要当table位置在Y2、Y3的时候,点击textField,让table移动到Y1的位置,但是这个searchBar总是马上就跳到最终的那个位置上,还是searchBar的textField状态为编辑状态时的问题。昨晚在CSDN提问题也没人来回答,今天本天才自己搞定了。(莫非人家是不屑回答这么小儿科的问题吗...)
Y1、Y2、Y3的位置在这里:
还是在delegate里面找到的方法(学会查看delegate方法是一项多么重要的技能啊...):
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar;
运行测一下,发现
每次点击searchBar的textField时,都会走这个方法
返回false时, searchBar的textField点击没有反应; 默认返回true
由此,把思路调整为:点击textField时,让编辑状态失效(不要马上弹出键盘),然后当动画做完以后,再呼出键盘:
/** 每次点击searchBar的textField时,都会走这个方法 * 返回false时, searchBar的textField点击没有反应 * 默认返回true */ - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { if (!self.offsetY) { self.offsetY = Y3; } // 如果点击时,shadowView的y坐标 在Y2 Y3的位置, if (self.offsetY > Y1) { // NSLog(@"----------- y = %f ------------",self.offsetY); // ============ 触发block ============= if (self.didClickTextFieldBlock) { self.didClickTextFieldBlock(); } return false; } return true; }
这里的block是 控制table的那个控制器传给主控制器的事件的一个block,然后在主控制器里做动画:
[self.vc didClickTextField:^{ // NSLog(@"receive-----------"); [UIView animateWithDuration:0.4 animations:^{ self.shadowView.frame = CGRectMake(0, 50, self.view.frame.size.width, [UIScreen mainScreen].bounds.size.height); }completion:^(BOOL finished) { // 呼出键盘。 一定要在动画结束后调用,否则会出错 [self.vc.searchController.searchBar becomeFirstResponder]; }]; // 更新offsetY self.vc.offsetY = self.shadowView.frame.origin.y; }];
这里注意:呼出键盘的方法一定要在动画结束时候的block里面设置,否则也会出错。
以上的方法是我目前能够想到并且可以完美解决我遇到的问题的方法,如果你还有其他方法,欢迎纠错,我会及时更新的。
下载
源码在这里