码迷,mamicode.com
首页 > 其他好文 > 详细

从头自定义SearchBar和SearchDisplayController

时间:2016-01-02 14:06:25      阅读:292      评论:0      收藏:0      [点我收藏+]

标签:

自定义UISearchBar和UISearchDisplayController

 

起因

近期由于公司项目升级,UI变化较大,之前项目中的搜索一直是使用的系统自带组件UISearchBar和UISearchDisplayController,然而根据UI设计图,该系统组件无法满足需求,故需要自定义该组件以实现类似微信的搜索功能,在百度无果的情况下,终于决定自己动手完全从头自定义,特写此文以提供思路给有同样需求的同行。

步骤

  1. 实现CustomSearchTextField相关组件
  2. 实现CustomSearchBar相关组件
  3. 实现CustomSearchDisplayController相关组件

效果图

按照惯例先上几张效果图,如下:
技术分享
由于重点在于搜索控件,所以tableView中的数据并非真实数据。

主要实现

实现CustomSearchTextField控件

由于我需要的效果是搜索框图片位于左边和中央的位置,而且还需要一层边框,所以CustomSearchTextField主要组成有:CustomSearchTextFieldBackgroundView(背景层),UISearchTextField(输入框),UIImageView(搜索提示按钮),UILabel(占位文本标签)

1 @interface CMSearchTextField ()
2 @property(nonatomic, strong) UIButton* _searchTextClearButton;<br/>
3 @property(nonatomic, strong) UIView* _searchTextFieldBackgroudView;<br/>
4 @property(nonatomic, strong) UILabel* _searchTextFieldPlaceHolderLabel;<br/>
5 @property(nonatomic, strong) UIImageView* _searchTextFieldIcon;<br/>
6 @property(nonatomic, strong) UITextField* _searchTextField;<br/>
7 @end

 

省略了各个UI控件的初始化代码,layoutSubviews中的代码如下:

layoutSubViews代码

 1 [UIView animateWithDuration:0.25f animations:^{
 2     [__searchTextFieldBackgroudView setFrame:self.bounds];
 3     if(__searchTextField.isFirstResponder) {
 4         [__searchTextFieldIcon setFrame:CGRectMake(10, __searchTextFieldIcon.frame.origin.y, __searchTextFieldIcon.frame.size.width, __searchTextFieldIcon.frame.size.height)];
 5         [__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)];
 6         [__searchTextFieldPlaceHolderLabel setFrame:__searchTextField.bounds];
 7         }else {
 8             [__searchTextFieldIcon setCenter:CGPointMake(__searchTextFieldBackgroudView.bounds.size.width / 2 - 10, __searchTextFieldBackgroudView.bounds.size.height / 2)];
 9             [__searchTextField setFrame:__searchTextFieldBackgroudView.bounds];
10             [__searchTextFieldPlaceHolderLabel setFrame:CGRectMake(__searchTextFieldBackgroudView.bounds.size.width / 2, __searchTextField.frame.origin.y, __searchTextFieldBackgroudView.bounds.size.width / 2 - 5, __searchTextField.bounds.size.height)];
11     }
12 }]; 

layoutSubviews代码说明

该段代码主要是做了两件事
1. 如果该UITextField是firstResponder : 调整各个控件的frame,以动画形式使得搜索图标位于左侧,UITextField调整至合适大小,UILabel也调整到文本框左侧最开始处
2. 如果该UITextField不是firstResponder : 调整各个控件的frame,以动画形式使得UITextField的frame充满整个背景层,搜索图标和UILabel调整至整个CustomSearchTextField的正中央

实现CustomSearchBar相关组件

参考UISearchBar头文件,该类主要实现了一个基本的SearchBar,包括对UITextField的基本状态的处理,留出外部调用的代理接口。

CustomSearchBar主要组成控件

CustomSearchTextField主要组成是由一个背景层,一个搜索框,上下两条分隔线,取消按钮等组成,如下代码所示:

1 @interface CMSearchBar () <CMSearchTextFieldDelegate><br/>
2 @property(nonatomic, strong) CMSearchTextField* searchTextField;<br/>
3 @property(nonatomic, strong) UIView* searchBarBackgroudView;<br/>
4 @property(nonatomic, strong) UIImageView* searchBarTopSeperatorLine;<br/>
5 @property(nonatomic, strong) UIImageView* searchBarBottomSeperatorLine;<br/>
6 @property(nonatomic, strong) UIButton* cancelSearchButton;<br/>
7 @property(nonatomic, assign) BOOL shouldShowCancelButton;<br/>
8 @end

而由于本人水平所限,不知道系统的UISearchBar是如何做到看上去似乎与UISearchDisplayController毫无关联,故我在CustomSearchBar中保留了一个CustomSearchDisplayController属性,使得在输入框状态变化时能调用CustomSearchDisplayController中的相关代理方法,如下代码所示:

@interface CMSearchBar : 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)];
        ifshouldShowCancelButton) {
            [_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, <u>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类情况如下所示:

 1 @interface CMSearchDisplayController : NSObject
 2 -(instancetype)initWithSearchBar:(CMSearchBar*)searchBar contentsController:(UIViewController*)viewController;
 3 @property(nonatomic, strong, readonly) CMSearchBar* searchBar;
 4 @property(nonatomic, strong, readonly) UIViewController* searchContentsController;
 5 @property(nonatomic, strong, readonly) UITableView* searchResultsTableView;
 6 @property(nonatomic, assign) id<UITableViewDataSource> searchResultsDataSource;
 7 @property(nonatomic, assign) id<UITableViewDelegate> searchResultsDelegate;
 8 @property(nonatomic, assign) id<CMSearchDisplayControllerDelegate> delegate;
 9 @property (nonatomic, assign) BOOL isActive;
10 -(void)setActive:(BOOL)bActive;
11 -(void)setActive:(BOOL)bActive animated:(BOOL)animated;
12 @end

其中主要包括一个searchBar,一个tableView,一个contentsController实例,还有用于tableview的dataSource和delegate的两个代理及用于外部实现的SearchDisplayControllerDelegate,其中最主要的代码便要数setActive:animated中的处理,如下所示:

 1 -(void)setActive:(BOOL)bActive animated:(BOOL)animated {
 2     isActive = bActive;
 3     CGFloat animateDuring = animated ? 0.25f : 0.0f;
 4 if(searchContentsController.navigationController == nil)
 5         return ;
 6     if(bActive) {
 7         [_searchContentsController.navigationController.navigationBar setFrame:CGRectMake(0, -44, _searchContentsController.navigationController.navigationBar.bounds.size.width, _searchContentsController.navigationController.navigationBar.bounds.size.height)];
 8         UIImageView* dimmView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 44, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
 9         [dimmView setUserInteractionEnabled:YES];
10         [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]];
11         [[UIApplication sharedApplication].keyWindow addSubview:dimmView];
12         _searchBarPreviousSuperView = _searchBar.superview;
13         _searchBarPreviousSuperRect = searchBar.frame;
14         [searchBar removeFromSuperview];
15         [dimmView addSubview:searchBar];
16         [searchBar setFrame:CGRectMake(0, 0, _searchBar.bounds.size.width, searchBar.bounds.size.height + 20)];
17         [UIView animateWithDuration:animateDuring animations:{
18             [searchContentsController.view setFrame:CGRectMake(0, -44, _searchContentsController.view.bounds.size.width, searchContentsController.view.bounds.size.height + 44)];
19             [dimmView setFrame:CGRectMake(0, 0, dimmView.bounds.size.width, dimmView.bounds.size.height)];
20         } completion:BOOL finished {
21             if(finished) {
22                 dispatch_async(dispatch_get_main_queue(), {
23                     [searchResultsTableView setFrame:CGRectMake(0, _searchBar.bounds.size.height, dimmView.bounds.size.width, dimmView.bounds.size.height - searchBar.bounds.size.height)];
24                     [dimmView addSubview:searchResultsTableView];
25                     if([self.delegate respondsToSelector:@selector(searchDisplayControllerDidBeginSearch:)])
26                         [self.delegate searchDisplayControllerDidBeginSearch:self];
27                 });
28             }
29         }];
30     }else {
31         [searchResultsTableView removeFromSuperview];
32         [UIView animateWithDuration:0.25f animations:{
33             [searchContentsController.navigationController.navigationBar setFrame:CGRectMake(0, 20, _searchContentsController.navigationController.navigationBar.bounds.size.width, searchContentsController.navigationController.navigationBar.bounds.size.height)];
34             [searchBar.superview setFrame:CGRectMake(0, 64, _searchBar.superview.bounds.size.width, searchBar.superview.bounds.size.height)];
35             [searchContentsController.view setFrame:CGRectMake(0, 0, _searchContentsController.view.bounds.size.width, searchContentsController.view.bounds.size.height - 44)];
36             [searchBar setFrame:CGRectMake(0, 0, _searchBar.bounds.size.width, searchBar.bounds.size.height - 20)];
37         }completion:BOOL finished {
38             if(finished) {
39                dispatch_async(dispatch_get_main_queue(), {
40                     [searchBar.superview removeFromSuperview];
41                     [searchBarPreviousSuperView addSubview:searchBar];
42                     if([self.delegate respondsToSelector:@selector(searchDisplayControllerDidEndSearch:)])
43                         [self.delegate searchDisplayControllerDidEndSearch:self];
44                 });
45             }
46         }];
47     }
48     return ;
49 }

setActive:animate:主要代码说明

contentsController指的是内容展示viewController,一般传递的是持有者本身,在此方法中主要做了以下几件事:
1. 如果contentsController没有导航栏,则不需要调整任何控件
2. 如果contentsController有导航栏,且处于激活状态
使用动画方式使得该UINavigationBar实例往上移动44像素个单位,然后创建遮罩层,为方便起见,我直接将他加在当前的keyWindow当中,记录CustomSearchBar的superView及在superView中的frame,再将它从父视图中移除,并加入到遮罩层dimmView当中,调整坐标为0,0,拉长20个高度以覆盖整个状态栏,显示取消按钮。
3. 如果contentsController有导航栏,且自于未激活状态
使用动画的方式使得该UINavigationBar实例重新移动到合适位置,然后遮罩层同步下移同样的高度,调整CustomSearchBar的高度,使之减少20个像素单位,回到未激活状态的位置,动画完成之后,删除遮罩层dimmView,CustomSearchBar从其superView(dimmView)中移出,并加入到原来它的superView当中,并设置位置为在原来superView当中的位置,隐藏取消按钮。

结语

由于本人水平所限,代码写的并不十分美观,也并不十分高效,对于iOS大神来说,肯定会有更好的解决方案,本文旨在为那些需要此功能但还没有思路的同行们提供一点参考,iOS大神请无视,最后感谢所有阅读此文的人。

从头自定义SearchBar和SearchDisplayController

标签:

原文地址:http://www.cnblogs.com/wangluofan/p/5094453.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!