如何基于UISearchController深度自定義搜索UI

一、搜索功能我相信很多app都用得上,iOS系統(tǒng)提供做搜索的類有那幾種呢?

1.UISearchBar,這個(gè)相信大多數(shù)開發(fā)者都用過,這個(gè)只是提供搜索框,其實(shí)就是跟一個(gè)UITextField沒啥區(qū)別,只是在textfield封裝了多點(diǎn)內(nèi)容。

2.UISearchDisplayController,這個(gè)玩意是iOS8之前,專門用來(lái)做搜索功能,對(duì)的,它就是一個(gè)控制器,他為你提供蘋果那種搜索風(fēng)格的轉(zhuǎn)場(chǎng)動(dòng)畫以及搜索視圖顯示邏輯等,但是使用起來(lái)相對(duì)復(fù)雜。所以在iOS8這個(gè)類已經(jīng)被拋棄了,而引申一個(gè)新的搜索類,就是以下要介紹的UISearchController。

3.UISearchController,這個(gè)類其實(shí)跟UISearchDisplayController功能差不多,只是蘋果對(duì)其再進(jìn)行了高級(jí)封裝,從而使用起來(lái)更加簡(jiǎn)單了。

二、其實(shí)自己在搞UISearchController之前,我已經(jīng)到網(wǎng)上找過一下相關(guān)的資料,發(fā)現(xiàn)網(wǎng)上寫的都是千遍一律,教你怎么用,就我個(gè)人覺得,難的是你要在它基礎(chǔ)上去對(duì)改它的UI,這就要你對(duì)它的視圖層次結(jié)構(gòu)要很清楚了,那我們下面來(lái)開始干活吧。

我們首先看一下系統(tǒng)自帶的搜索框是怎樣的。


normal狀態(tài)下的searchBar

這個(gè)是正常狀態(tài)下的搜索框,但是那灰色的背景真是丑出翔了,試問有那個(gè)產(chǎn)品設(shè)計(jì)能接受這個(gè)樣式的搜索框?我們?cè)倏聪戮庉嫚顟B(tài)下的吧。


編輯無(wú)文字狀態(tài)下


從normal到edit狀態(tài)會(huì)有一個(gè)轉(zhuǎn)場(chǎng)動(dòng)畫,雖然界面還是很丑,而我們需要的正式這個(gè)動(dòng)畫,如果我們完全自定義的話還需要做這個(gè)動(dòng)畫就未免有點(diǎn)復(fù)雜了。


編輯有文字狀態(tài)

當(dāng)你輸入文字是,你發(fā)現(xiàn)它那個(gè)蒙版消失了,而展現(xiàn)新的視圖,沒錯(cuò),那個(gè)就是你自己的搜索結(jié)果控制器視圖了,下面再詳細(xì)介紹。

三、看了上面幾張圖,我們大概知道UISearchController的交互大概怎樣。

那我們?cè)龠M(jìn)去頭文件看一下他的一些屬性和方法吧

它的指定初始化方法就是

```

- (instancetype)initWithSearchResultsController:(nullableUIViewController*)searchResultsController;

```

所以你能定制屬于你自己的搜索結(jié)果view。當(dāng)你傳nil的時(shí)候,默認(rèn)是當(dāng)前view就是搜索結(jié)果view。

UISearchResultsUpdating -> 搜索結(jié)果更新回調(diào)的協(xié)議,實(shí)現(xiàn)里面的方法就能更新搜索邏輯了,這里不詳細(xì)說(shuō),網(wǎng)上一大堆教這個(gè)的。

再說(shuō)下比較常用的屬性吧

```

dimsBackgroundDuringPresentation -> 是否顯示灰色透明的蒙版,默認(rèn)YES

hidesNavigationBarDuringPresentation -> 是否隱藏導(dǎo)航條,這個(gè)一般不需要管,都是隱藏的

searchResultsController -> 就是你初始化傳進(jìn)去的搜索結(jié)果VC

searchBar -> 它內(nèi)部會(huì)創(chuàng)建一個(gè)搜索框給你。

UISearchController就這幾個(gè)屬性了。我們看下先怎么去用?

```

UISearchController *searchController= [[UISearchController alloc] init]; ?

self.tableView.tableHeaderView=searchController.searchBar;

searchController.searchResultsUpdater=self;

searchController.searchBar.delegate=self;

searchController.searchBar.placeholder=@"搜索"; // placeholder

[searchController.searchBar setSearchFieldBackgroundImage:[UIImagehcq_imageNamed:@"business_search_bg"] forState:UIControlStateNormal]; // 設(shè)置搜索框內(nèi)部textField的背景圖

[self.searchBar setBackgroundImage:@""] // 設(shè)置搜索框背景圖,要跟上面的區(qū)分哦,兩者不一樣

[searchController.searchBarsetImage:[UIImagehcq_imageNamed:@"business_search_icon"] forSearchBarIcon:UISearchBarIconSearchstate:UIControlStateNormal];// 設(shè)置搜索框內(nèi)放大鏡圖片

searchController.searchBar.tintColor=KC_RGB_COLOR(225,225,225);// 設(shè)置搜索框內(nèi)按鈕文字顏色,以及搜索光標(biāo)顏色。

searchController.searchBar.barTintColor=HCQ_VIEW_BACKGROUND_COLOR;// 設(shè)置搜索框背景顏色

// Get the instance of the UITextField of the search bar

// 用KVC修改placeholder文字顏色

UITextField*searchField = [self.searchBarvalueForKey:@"_searchField"]; // 先取出textfield

// Change the search bar placeholder text color

[searchFieldsetValue:self.searchBar.tintColorforKeyPath:@"_placeholderLabel.textColor"]; // 然后setValueForKey,搞定

[searchController.searchBarsetValue:@"完成"forKey:@"_cancelButtonText"]; // 設(shè)置搜索框那個(gè)取消按鈕文字

// 如果你不想要搜索框的背景或者希望背景透明,你加上這句代碼吧

[[[searchController.searchBar.subviews.firstObject subviews] firstObject] removeFromSuperview];// 直接把背景imageView干掉。在iOS8,9是沒問題的,7沒測(cè)試過。

```

到這里,UISearchBar的UI就自定義完畢,想了解UISearchBar層次結(jié)構(gòu)的話,可以用Xcode運(yùn)行后打開那個(gè)視圖層次結(jié)構(gòu)看一下就一目了然。

四、下面介紹怎樣定義編輯無(wú)文字狀態(tài)下的搜索UI,例如微信點(diǎn)擊搜索框后會(huì)出現(xiàn)3個(gè)按鈕《朋友圈,文章,公眾號(hào)》。

要定義這些UI,那么我們需要自定義一個(gè)自己的searchController了,創(chuàng)建一個(gè)類MySearchController繼承UISearchController。

首先我們重寫初始化方法

```

- (instancetype)initWithSearchResultsController:(UIViewController*)searchResultsController

{

MyResultViewController *resultVC = [[MyResultViewController ? alloc] init];

if(self= [super initWithSearchResultsController: resultVC]) {

[selfsetup];

}

returnself;

}

- (instancetype)init

{

MyResultViewController *resultVC = [[MyResultViewController? alloc] init];

if(self= [super initWithSearchResultsController:resultVC]) {

? ? ? [selfsetup];

}

returnself;

}

// 重寫init方法,好讓外部怎么創(chuàng)建都是我們自己的搜索結(jié)果控制器。外部使用不需要關(guān)心太多

// 可以把剛才自定義SearchBarUI代碼放到內(nèi)部了。

- (void)setup

{

self.searchBar.placeholder=@"搜索商家";

[self.searchBarsetSearchFieldBackgroundImage:[UIImagehcq_imageNamed:@"business_search_bg"]forState:UIControlStateNormal];

[self.searchBarsetImage:[UIImagehcq_imageNamed:@"business_search_icon"]forSearchBarIcon:UISearchBarIconSearchstate:UIControlStateNormal];

self.searchBar.tintColor=KC_RGB_COLOR(225,225,225);

self.searchBar.barTintColor=HCQ_VIEW_BACKGROUND_COLOR;

// Get the instance of the UITextField of the search bar

UITextField*searchField = [self.searchBarvalueForKey:@"_searchField"];

// Change the search bar placeholder text color

[searchFieldsetValue:self.searchBar.tintColorforKeyPath:@"_placeholderLabel.textColor"];

[[[self.searchBar.subviews.firstObjectsubviews]firstObject]removeFromSuperview];

[self.searchBarsetValue:@"完成"forKey:@"_cancelButtonText"];

}

```

然后來(lái)到viewDidLoad方法,添加我們需要定義的UI。

例如我想加一個(gè)switch到中間

```

UISwitch*st = [UISwitch new];

[self.view addSubview:st];

st.center = self.view.center;

```

一般我們會(huì)這樣寫,運(yùn)行后點(diǎn)擊搜索框進(jìn)入編輯狀態(tài),發(fā)現(xiàn)開關(guān)出來(lái)了,但是當(dāng)你輸入文字的時(shí)候,開關(guān)還在顯示還能點(diǎn)擊。這就神奇了。會(huì)不會(huì)是視圖層級(jí)不對(duì)呢?沒錯(cuò),就是視圖層級(jí)問題,SearchController.view內(nèi)部還有一個(gè)containerView(其實(shí)就是那個(gè)蒙版),而SearchController是把搜索結(jié)果控制器view添加到containerView上,所以開關(guān)跟containerView是同一層次,而且在containerView之上,所以不會(huì)消失。也可以打開層次結(jié)果圖看一下。

我們可以添加一個(gè)屬性

```

@property(nonatomic,weak)UIView*containerView;

```

重寫get方法

```

- (UIView*)containerView

{

if(!_containerView) {

_containerView=self.view.subviews.firstObject;

_containerView.backgroundColor=HCQ_VIEW_BACKGROUND_COLOR;

}

return_containerView;

}

```

然后在viewdidload中添加開關(guān)的代碼改一下

```

[self.containerView addSubview:st];

```

繼續(xù)運(yùn)行,發(fā)現(xiàn)還是不行,還是不會(huì)消失,為什么呢?我們知道view的subview有先后順序,后添加的越在上面,因?yàn)槟愕乃阉鹘Y(jié)果view先添加到containerView,開關(guān)是后面加的,固然開關(guān)還是能看到。所以,你不能直接add,正確姿勢(shì)是插入insert

```

[self.containerView insertSubview:st atIndex:0];

```

這樣就沒問題了。

再補(bǔ)充一點(diǎn),如果你想改containerView的frame,可以在這個(gè)方法修改

```

- (void)viewDidLayoutSubviews

{

[super viewDidLayoutSubviews];

}

```

如果想做一些轉(zhuǎn)場(chǎng)過度動(dòng)畫,那么重寫viewWillAppear、viewWillDisappear在里邊做吧

例如

```

- (void)viewWillAppear:(BOOL)animated

{

[superviewWillAppear:animated];

// 修改textfield背景圖

[self.searchBar setSearchFieldBackgroundImage:[UIImagehcq_imageNamed:@"business_search_bg_highlighted"] forState:UIControlStateNormal];

}

- (void)viewWillDisappear:(BOOL)animated

{

[superviewWillDisappear:animated];

// 這里再改回來(lái)

[self.searchBar setSearchFieldBackgroundImage:[UIImagehcq_imageNamed:@"business_search_bg"]forState:UIControlStateNormal];

}

```

如果當(dāng)狀態(tài)為activity的時(shí)候想隱藏tabbar,建議實(shí)現(xiàn)UISearchControllerDelegate的代理方法

```

func willPresentSearchController(_searchController:UISearchController) {

tabBarController?.tabBar.isHidden=true

}

func willDismissSearchController(_searchController:UISearchController) {

tabBarController?.tabBar.isHidden=false

}

然后在viewwillAppear將tabbar再次隱藏,否則pop回來(lái)的時(shí)候又會(huì)出現(xiàn)

override func viewWillAppear(_animated:Bool) {

super.viewWillAppear(animated)

if searchController.isActive{

? ? ?tabBarController?.tabBar.isHidden = true

}

}

```

如果想push的時(shí)候把搜索框也跟著隱藏

那么在導(dǎo)航的棧頂控制器寫下

definesPresentationContext = true

必須要在棧頂控制器寫,而且push下一個(gè)子控制器的時(shí)候也要在導(dǎo)航棧里Push,否則會(huì)有莫名其妙的Bug

五、結(jié)語(yǔ)

基本上按照上面所說(shuō)的就能自定義基于系統(tǒng)的,屬于自己風(fēng)格的搜索框了。用系統(tǒng)提供的轉(zhuǎn)場(chǎng)動(dòng)畫還是挺好看的。定制起來(lái)也比較簡(jiǎn)單,有這個(gè)需求的可以參考下我寫的這編文章。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容