iOS UISearchBar实现

项目需求要求实现带历史记录的搜索功能,经过无数次网上查找资料和几次修改,终于实现了,菜鸟一枚,需记录下来以便以后查阅。代码中的布局使用Masonry,另外还使用了ReactiveCocoa来处理删除某条历史记录、清空历史记录等操作的消息传递。

搜索框UI自定义

A574C7A4-8948-4BC9-B033-58944A3AD32C.png

上图是要实现的效果,下面来看看具体的实现。这里我使用的是系统的UISearchBar控件,首先要考虑的是如何实现搜索框背景颜色,光标颜色的改变,以及如何将系统放大镜等图片替换成我们想要的样子。
添加UISearchBar在VC上,运行一下,我们可以看到其效果:


_searchBar = [[UISearchBar alloc] init];
_searchBar.backgroundColor = [UIColor clearColor];
_searchBar.showsCancelButton = NO;
[_searchBgView addSubview:_searchBar];

3C74223F-D9A6-4DE0-A1A0-F387D4C4FEC7.png

发现将searchBar的背景颜色设置为clearColor并不能去掉其灰色的边框,查看文档,发现其还有两个关于颜色设置的属性,其中tintColor可以用来改变输入光标的的颜色,另外文档里说可以使用-barTintColor来调整搜索框的背景颜色。

/*
 The behavior of tintColor for bars has changed on iOS 7.0. It no longer affects the bar's background
 and behaves as described for the tintColor property added to UIView.
 To tint the bar's background, please use -barTintColor.
 */
@property(null_resettable, nonatomic,strong) UIColor *tintColor;
@property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  // default is nil

于是设置searchBar.barTintColor = [UIColor whiteColor];运行效果如下图所示:

C6000359-8D0A-4315-9D81-4B8AD4861358.png

结果是出现上线两条线,并没有完全消除背景颜色,根据网友资料,在控制台通过命令打印出UISearchBar的视图层次结构如下:

<UISearchBar: 0x7fb6586b6ca0; frame = (10 30; 355 30); text = ''; tintColor = UIDeviceRGBColorSpace 1 0.5 0 1; gestureRecognizers = <NSArray: 0x7fb6586bab20>; layer = <CALayer: 0x7fb658609540>>
   | <UIView: 0x7fb6586b9e00; frame = (0 0; 355 30); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x7fb65869f110>>
   |    | <UISearchBarBackground: 0x7fb6586baff0; frame = (0 0; 355 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fb6586bb4c0>>
   |    | <UISearchBarTextField: 0x7fb658525ce0; frame = (0 0; 0 0); text = ''; clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x7fb658505960>>
   |    |    | <_UISearchBarSearchFieldBackgroundView: 0x7fb658419960; frame = (0 0; 0 0); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x7fb658409dc0>>

可以移除UISearchBarBacground来移除背景颜色

        for (UIView *subView in _searchBar.subviews) {
            if ([subView isKindOfClass:[UIView  class]]) {
                [[subView.subviews objectAtIndex:0] removeFromSuperview];
            }
        }

移除了背景颜色,接下来要修改输入框的背景颜色和放大镜图片,此时UIView中就剩下一个UITextField,系统默认的放大镜图片可通过设置UITextField的leftView来设置,打印UITextField的leftView,可以看出其是一个UIImageView,具体信息如下

(lldb) po textField.leftView
<UIImageView: 0x7f8df3f1b870; frame = (0 0; 13 13); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7f8df3f13d40>>

具体实现代码和实现效果如下:

        _searchBar = [[UISearchBar alloc] init];
        _searchBar.backgroundColor = [UIColor clearColor];
        _searchBar.showsCancelButton = NO;
        _searchBar.tintColor = [UIColor orangeColor];
        _searchBar.placeholder = @"搜索感兴趣的内容";
        
        for (UIView *subView in _searchBar.subviews) {
            if ([subView isKindOfClass:[UIView  class]]) {
                [[subView.subviews objectAtIndex:0] removeFromSuperview];
                if ([[subView.subviews objectAtIndex:0] isKindOfClass:[UITextField class]]) {
                    UITextField *textField = [subView.subviews objectAtIndex:0];
                    textField.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
                    
                    //设置输入框边框的颜色
//                    textField.layer.borderColor = [UIColor blackColor].CGColor;
//                    textField.layer.borderWidth = 1;
                    
                    //设置输入字体颜色
//                    textField.textColor = [UIColor lightGrayColor];
                    
                    //设置默认文字颜色
                    UIColor *color = [UIColor grayColor];
                    [textField setAttributedPlaceholder:[[NSAttributedString alloc] initWithString:@"搜索感兴趣的内容"
                                                                                        attributes:@{NSForegroundColorAttributeName:color}]];
                    //修改默认的放大镜图片
                    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 13, 13)];
                    imageView.backgroundColor = [UIColor clearColor];
                    imageView.image = [UIImage imageNamed:@"gww_search_ misplaces"];
                    textField.leftView = imageView;
                }
            }
        }
        
        [_searchBgView addSubview:_searchBar];
4F12C076-C4B5-4D12-A980-6950BA32274C.png

有些需求可能会要求限制输入的次数,这里限制字数20,超出及给个弹窗提示,具体实现如下:

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
    if ([searchText length] > 20) {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:@"字数不能超过20" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil];
        [alertController addAction:alertAction];
        [self presentViewController:alertController animated:nil completion:nil];
        [_searchBar setText:[searchText substringToIndex:20]];
    }
}

历史记录功能实现

搜索框已经按照我们想要的样式修改完毕,接下来要实现的是历史记录功能。搜索历史的弹出使用DXPopver,这里我是写了一个tableViewController放到popper中来进行历史记录的展示。首先我们需要定义一个数组来存储搜索记录,同时要将记录存储到端上,这里定义了一个单例用来存储记录:


#import "APPUserDefaults.h"

@implementation APPUserDefaults

+(id)instance
{
    static APPUserDefaults *_instance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        _instance = [[APPUserDefaults alloc] init];
    });
    return _instance;
}

-(void)setUserSearchHistory:(NSMutableArray *)array
{
    [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"SearchRecord"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

-(NSMutableArray *)getUserSearchHistory
{
    return [[NSUserDefaults standardUserDefaults] objectForKey:@"SearchRecord"];
}
@end

存储好搜索记录之后,每次点击搜索框,如果之前有历史记录就要显示,如没有则不用显示

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    // when we start to write sth, the record should display to us
    if (_historyArray.count != 0) {
        _historyViewController.historyRecords = _historyArray;
        [self showTheHistoryRecords];
        [self.searchBar becomeFirstResponder];
    }
}
- (void)showTheHistoryRecords
{
    CGPoint startPoint = CGPointMake(CGRectGetWidth(self.view.frame), 64);
    [self.popover showAtPoint:startPoint
               popoverPostion:DXPopoverPositionDown
              withContentView:self.historyViewController.view
                       inView:self.view];
    self.popover.didDismissHandler = ^{
        
    };
}

- (void)hiddenTheHistoryRecords
{
    [self.popover dismiss];
}

搜索的时候要记录搜索历史,在点击空格键,在点击搜索时,没有匹配的内容出现,但是搜索历史里面会留下一行“空白”的记录,点击多下空格时,同样会出现这个问题,然而我们的搜索历史并不需要存储这个空格。

EB317CA8-DE59-48DD-8263-6B6F2CF28EF2.png

这里我们就要识别输入的字符是不是空格,如果是空格,就不对其进行存储,因为可能会出现多个空格的情况,就不能简单的使用

[_searchBar.text isEqualToString:@" "];

这里使用NSPredicate谓词来判断输入的字符是否为一个或者多个空格

NSString *regex = @"\\s*";

这里\s是正则表达式,匹配任何空白字符
*,匹配当期表达式一次或者多次

    NSString *regex = @"\\s*";
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    if (![_historyArray containsObject:_searchBar.text] && ![pred evaluateWithObject:_searchBar.text]) {
        [_historyArray insertObject:_searchBar.text atIndex:0];
    }
    

本地内容及时搜索

许多 app 都支持对本地内容的及时搜索,这里简单实现了该功能,直接上代码

    words = [words uppercaseString];  //搜索关键字,可以是中文,也可以是拼音或者缩写
    NSMutableArray *array = [NSMutableArray array]; //用来存储搜索结果
    //self.dataArray 中存储着本地信息,逐项对比,看是不是我们所要的结果,self.cachePinyinDict 中存储着本地信息的拼音转换结果 例如 影视对应 yingshi ys
    for (NSString *name in self.dataArray) {
        NSString *pinyin = [self.cachePinyinDict objectForKey:name];
        pinyin = pinyin.uppercaseString;
        if ([name rangeOfString:words].length > 0) {
            [array addObject:name];
        }else if ([pinyin rangeOfString:words].length > 0 && [pinyin rangeOfString:words].location == 0) {
            [array addObject:name];
        }
    }

拼音的转化采用了第三方 PinyinHelper 工具

- (void)cachePinyin
{
    if (!self.dataArray.count) return;
    
    for (NSString *name in self.dataArray) {
        NSString *p = [self.cachePinyinDict objectForKey:name];
        if (p == nil) {
            p = [[PinyinHelper getPinyin:name  isXingshi:NO] join:@" "];
            self.cachePinyinDict[name] = p;
        }
    }
}

具体实现结果

9FC0286E-6CEC-47EB-8678-B55E152054EC.png

具体的功能已经介绍差不多了,具体demo地址github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • Swift版本点击这里欢迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh阅读 25,262评论 7 249
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,391评论 25 707
  • 不是因为年纪大了,不是因为朋友都结婚了,还有的生孩子了,不是因为家人着急了,不是因为所有人觉得我应该了;我一直都期...
    砰砰砰_砰阅读 155评论 1 0
  • 今天的早课,是查兰英老师的《迩之事父,远之事君》,这个主题给我的第一印象是,好有诗意,所以在做图片的时候,特别找了...
    叶赖子阅读 373评论 3 1