自定义UISearchBar和UISearchDisplayController
来源:互联网 发布:计算机c语言二级考试 编辑:程序博客网 时间:2024/05/18 02:35
自定义UISearchBar和UISearchDisplayController
- 自定义UISearchBar和UISearchDisplayController
- 起因
- 步骤
- 效果图
- 主要实现代码
- 实现SearchTextField组件及代理
- layoutSubviews代码说明
- 实现CustomSearchBar相关组件
- CustomSearchBar的主要组成
- layoutSubviews代码说明
- 实现CustomSearchDisplayController相关组件
- CustomSearchDisplayController类组成
- setActiveanimate主要代码说明
- 实现SearchTextField组件及代理
- 结语
起因
近期由于公司项目升级,UI变化较大,之前项目中的搜索一直是使用的系统自带组件UISearchBar和UISearchDisplayController,然而根据UI设计图,该系统组件无法满足需求,故需要自定义该组件以实现类似微信的搜索功能,在百度无果的情况下,终于决定自己动手完全从头自定义,特写此文以提供思路给有同样需求的同行。
步骤
- 定义SearchTextField相关组件和代理
- 定义SearchBar相关组件和代理
- 定义SearchDisplayController相关组件和代理
效果图
按照惯例先上一张效果图,如下所示,由于重点在于搜索控件,所以tableView中的数据并非真实数据。
主要实现代码
实现SearchTextField组件及代理
由于我需要的效果是搜索框图片位于左边和中央的位置,而且还需要一层边框,所以CustomSearchTextField主要组成有:CustomSearchTextFieldBackgroundView(背景层),UISearchTextField(输入框),UIImageView(搜索提示按钮),UILabel(占位文本标签)
@interface CustomSearchTextField ()@property(nonatomic, strong) UIButton* _searchTextClearButton;@property(nonatomic, strong) UIView* _searchTextFieldBackgroudView;@property(nonatomic, strong) UILabel* _searchTextFieldPlaceHolderLabel;@property(nonatomic, strong) UIImageView* _searchTextFieldIcon;@property(nonatomic, strong) UITextField* _searchTextField;@end
省略了各个UI控件的初始化代码,主要的布局代码在layoutSubviews中,代码如下所示:
[UIView animateWithDuration:0.25f animations:^{ [__searchTextFieldBackgroudView setFrame:self.bounds]; if(__searchTextField.isFirstResponder) { [__searchTextFieldIcon setFrame:CGRectMake(10, __searchTextFieldIcon.frame.origin.y, __searchTextFieldIcon.frame.size.width, __searchTextFieldIcon.frame.size.height)]; [__searchTextField setFrame:CGRectMake(__searchTextFieldIcon.frame.origin.x + __searchTextFieldIcon.frame.size.width + 5, __searchTextField.frame.origin.y, __searchTextFieldBackgroudView.bounds.size.width - __searchTextFieldIcon.frame.origin.x - __searchTextFieldIcon.frame.size.width - 15, __searchTextField.frame.size.height)]; [__searchTextFieldPlaceHolderLabel setFrame:__searchTextField.bounds]; }else { [__searchTextFieldIcon setCenter:CGPointMake(__searchTextFieldBackgroudView.bounds.size.width / 2 - 10, __searchTextFieldBackgroudView.bounds.size.height / 2)]; [__searchTextField setFrame:__searchTextFieldBackgroudView.bounds]; [__searchTextFieldPlaceHolderLabel setFrame:CGRectMake(__searchTextFieldBackgroudView.bounds.size.width / 2, __searchTextField.frame.origin.y, __searchTextFieldBackgroudView.bounds.size.width / 2 - 5, __searchTextField.bounds.size.height)]; }}];
layoutSubviews代码说明
该段代码主要是做了两件事
如果该UITextField是firstResponder : 调整各个控件的frame,以动画形式使得搜索图标位于左侧,UITextField调整至合适大小,UILabel也调整到文本框左侧最开始处
如果该UITextField不是firstResponder : 调整各个控件的frame,以动画形式使得UITextField的frame充满整个背景层,搜索图标和UILabel调整至整个CustomSearchTextField的正中央
SearchTextField的代码到此就结束了,主要就是做了对UITextField的再封装,及对图标和占位文本的处理等等。
实现CustomSearchBar相关组件
参考UISearchBar头文件,该类主要实现了一个基本的SearchBar,包括对UITextField的基本状态的处理,留出外部调用的代理接口。
CustomSearchBar的主要组成
CustomSearchTextField主要组成是由一个背景层,一个搜索框,上下两条分隔线,取消按钮等组成,这些控件都放在分类里面,外部不可见,如下代码所示:
@interface CustomSearchBar () <CustomSearchTextFieldDelegate>@property(nonatomic, strong) CMSearchTextField* searchTextField;@property(nonatomic, strong) UIView* searchBarBackgroudView;@property(nonatomic, strong) UIImageView* searchBarTopSeperatorLine;@property(nonatomic, strong) UIImageView* searchBarBottomSeperatorLine;@property(nonatomic, strong) UIButton* cancelSearchButton;@property(nonatomic, assign) BOOL shouldShowCancelButton;@end
而由于本人水平所限,不知道系统的UISearchBar是如何做到看上去似乎与UISearchDisplayController毫无关联,故我在CustomSearchBar中保留了一个CustomSearchDisplayController属性,使得在输入框状态变化时能调用CustomSearchDisplayController中的相关代理方法,故CustomSearchBar类如下代码所示:
@interface CustomSearchBar : UIView@property(nonatomic, copy) NSString* placeholder;@property(nonatomic, copy) NSString* text;@property(nonatomic, weak) CMSearchDisplayController* searchDisplayController;@property(nonatomic, assign) id<CMSearchBarDelegate> delegate;@end
其中最主要的frame调整依然是在layoutSubViews方法中,相关调整代码如下:
-(void)layoutSubviews { [UIView animateWithDuration:0.25f animations:^{ [_searchBarBackgroudView setFrame:self.bounds]; [_searchBarTopSeperatorLine setFrame:CGRectMake(0, 0, _searchBarBackgroudView.bounds.size.width, kSingleLine)]; [_searchBarBottomSeperatorLine setFrame:CGRectMake(0, _searchBarBackgroudView.bounds.size.height - kSingleLine, _searchBarBackgroudView.bounds.size.width, kSingleLine)]; if(_shouldShowCancelButton) { [_searchTextField setFrame:CGRectMake(20, 25, _searchBarBackgroudView.bounds.size.width - 60, _searchBarBackgroudView.bounds.size.height - 30)]; }else { [_cancelSearchButton setHidden:YES]; [_searchTextField setFrame:CGRectMake(20, 5, _searchBarBackgroudView.bounds.size.width - 40, _searchBarBackgroudView.bounds.size.height - 10)]; } } completion:^(BOOL finished) { if(finished) { dispatch_async(dispatch_get_main_queue(), ^{ if(_shouldShowCancelButton) { [_cancelSearchButton setHidden:NO]; [_cancelSearchButton setFrame:CGRectMake(_searchBarBackgroudView.bounds.size.width - 5 - _cancelSearchButton.bounds.size.width, _cancelSearchButton.frame.origin.y, _cancelSearchButton.bounds.size.width, _cancelSearchButton.bounds.size.height)]; [_cancelSearchButton setCenter:CGPointMake(_cancelSearchButton.center.x, _searchTextField.center.y)]; } }); } }]; return [super layoutSubviews];}
layoutSubviews代码说明
变量__shouldShowCancelButton,是用于指示是否显示取消按钮的BOOL变量,该按钮只有在激活状态下才会显示,故此变量可用于指示搜索框是否处于激活状态下(所谓激活状态就是指UITextField是否成为firstResponder),如果是处于激活状态下,调整整个CustomSearchBackgroundView子控件的frame下移20个单位,即状态栏的高度,如果处于未激活状态,则调整至CustomSearchBar的正中央。
实现CustomSearchDisplayController相关组件
CustomSearchDisplayController类组成
参考UISearchDisplayController类中的方法,CustomSearchDisplayController类情况如下所示:
@interface CMSearchDisplayController : NSObject-(instancetype)initWithSearchBar:(CMSearchBar*)searchBar contentsController:(UIViewController*)viewController;@property(nonatomic, strong, readonly) CMSearchBar* searchBar;@property(nonatomic, strong, readonly) UIViewController* searchContentsController;@property(nonatomic, strong, readonly) UITableView* searchResultsTableView;@property(nonatomic, assign) id<UITableViewDataSource> searchResultsDataSource;@property(nonatomic, assign) id<UITableViewDelegate> searchResultsDelegate;@property(nonatomic, assign) id<CMSearchDisplayControllerDelegate> delegate;@property (nonatomic, assign) BOOL isActive;-(void)setActive:(BOOL)bActive;-(void)setActive:(BOOL)bActive animated:(BOOL)animated;@end
其中主要包括一个searchBar,一个tableView,一个contentsController实例,还有用于tableview的dataSource和delegate的两个代理及用于外部实现的SearchDisplayControllerDelegate,其中最主要的代码便要数setActive:animated中的处理,如下所示:
-(void)setActive:(BOOL)bActive animated:(BOOL)animated { _isActive = bActive; CGFloat animateDuring = animated ? 0.25f : 0.0f; if(_searchContentsController.navigationController == nil) return ; if(bActive) { [_searchContentsController.navigationController.navigationBar setFrame:CGRectMake(0, -44, _searchContentsController.navigationController.navigationBar.bounds.size.width, _searchContentsController.navigationController.navigationBar.bounds.size.height)]; UIImageView* dimmView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 44, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)]; [dimmView setUserInteractionEnabled:YES]; [dimmView setImage:[[self createImageWithColor:[UIColor colorWithRed:247.0f / 255.0f green:247.0f / 255.0f blue:247.0f / 255.0f alpha:0.9f] size:dimmView.bounds.size] applyLightEffect]]; [[UIApplication sharedApplication].keyWindow addSubview:dimmView]; _searchBarPreviousSuperView = _searchBar.superview; _searchBarPreviousSuperRect = _searchBar.frame; [_searchBar removeFromSuperview]; [dimmView addSubview:_searchBar]; [_searchBar setFrame:CGRectMake(0, 0, _searchBar.bounds.size.width, _searchBar.bounds.size.height + 20)]; [UIView animateWithDuration:animateDuring animations:^{ [_searchContentsController.view setFrame:CGRectMake(0, -44, _searchContentsController.view.bounds.size.width, _searchContentsController.view.bounds.size.height + 44)]; [dimmView setFrame:CGRectMake(0, 0, dimmView.bounds.size.width, dimmView.bounds.size.height)]; } completion:^(BOOL finished) { if(finished) { dispatch_async(dispatch_get_main_queue(), ^{ [_searchResultsTableView setFrame:CGRectMake(0, _searchBar.bounds.size.height, dimmView.bounds.size.width, dimmView.bounds.size.height - _searchBar.bounds.size.height)]; [dimmView addSubview:_searchResultsTableView]; if([self.delegate respondsToSelector:@selector(searchDisplayControllerDidBeginSearch:)]) [self.delegate searchDisplayControllerDidBeginSearch:self]; }); } }]; }else { [_searchResultsTableView removeFromSuperview]; [UIView animateWithDuration:0.25f animations:^{ [_searchContentsController.navigationController.navigationBar setFrame:CGRectMake(0, 20, _searchContentsController.navigationController.navigationBar.bounds.size.width, _searchContentsController.navigationController.navigationBar.bounds.size.height)]; [_searchBar.superview setFrame:CGRectMake(0, 64, _searchBar.superview.bounds.size.width, _searchBar.superview.bounds.size.height)]; [_searchContentsController.view setFrame:CGRectMake(0, 0, _searchContentsController.view.bounds.size.width, _searchContentsController.view.bounds.size.height - 44)]; [_searchBar setFrame:CGRectMake(0, 0, _searchBar.bounds.size.width, _searchBar.bounds.size.height - 20)]; }completion:^(BOOL finished) { if(finished) { dispatch_async(dispatch_get_main_queue(), ^{ [_searchBar.superview removeFromSuperview]; [_searchBarPreviousSuperView addSubview:_searchBar]; if([self.delegate respondsToSelector:@selector(searchDisplayControllerDidEndSearch:)]) [self.delegate searchDisplayControllerDidEndSearch:self]; }); } }]; } return ;}
setActive:animate:主要代码说明
contentsController指的是内容展示viewController,一般传递的是持有者本身,在此方法中主要做了以下几件事:
如果contentsController没有导航栏,则不需要调整任何控件
如果contentsController有导航栏,且处于激活状态
使用动画方式使得该UINavigationBar实例往上移动44像素个单位,然后创建遮罩层,为方便起见,我直接将他加在当前的keyWindow当中,记录CustomSearchBar的superView及在superView中的frame,再将它从父视图中移除,并加入到遮罩层dimmView当中,调整坐标为0,0,拉长20个高度以覆盖整个状态栏,显示取消按钮。如果contentsController有导航栏,且自于未激活状态
使用动画的方式使得该UINavigationBar实例重新移动到合适位置,然后遮罩层同步下移同样的高度,调整CustomSearchBar的高度,使之减少20个像素单位,回到未激活状态的位置,动画完成之后,删除遮罩层dimmView,CustomSearchBar从其superView(dimmView)中移出,并加入到原来它的superView当中,并设置位置为在原来superView当中的位置,隐藏取消按钮。
结语
由于本人水平所限,代码写的并不十分美观,也并不是十分高效,iOS大神们肯定会有更好的解决方案,本文旨在为那些需要此功能但还没有思路的同行们提供一点意见,iOS大神请无视,最后感谢所有阅读此文的人。
- 自定义UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchDisplayController 和 UISearchBar
- UISearchBar和UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchBar 和 UISearchDisplayController
- UISearchBar和UISearchDisplayController
- UISearchDisplayController和UISearchBar
- iOS UISearchBar和UISearchDisplayController
- 电子技术之PN结
- getAttribute和getParameter的区别
- Hive总结(二)hive基本操作
- 新人工作日记(1)
- 怎么样在iOS开发中用ARC和MRC混编
- 自定义UISearchBar和UISearchDisplayController
- Hive总结(三)hive组件和执行过程
- 新人工作日记(2)
- 从零开始搭建Flask网站之零:从零开始
- Hive总结(五)hive日志
- webdriver获取input文本框内容
- 新人工作日记(3)
- 新人开发日记(4)
- Hive总结(七)Hive四种数据导入方式