
目次
iOSアプリで検索バーを実装する方法
今回は、iOSで検索バーを実装するにあたって個人的につまずいた点が結構あったので、備忘録兼ねて、そのカスタマイズ方法について書いていきます。
検索バーを実装する方法は主に以下の2つあります。
・UISearchBarを使う方法
・自力実装する方法
大抵の場合は、UIKitにある標準のUISearchBarで事足りるかと思います。
僕も普通に使うのは大抵こちらです。
UISearchBarはその名の通り、Delegateやプロパティが検索用として設定できるので、ほぼこちらを使う方が大半だと思います。
もう一つの自力実装するやり方は、UITextFieldを実装するやり方や、あまりないですが、UITextViewを使ったやり方があるかと思います。
検索を実装する際は、大抵の場合で1行表示のみかつトップに設置するのでUITextFieldを使用することは滅多にないかと。
さっそく見ていきましょう!
UISearchBarの基本的なプロパティ
まずUISearchBarの基本的なプロパティから見ていきます。
barStyle | SearchBarの背景色のスタイル .default:灰色 .black:黒色 |
delegate | コールバック通知用 |
text | 検索入力欄のテキスト |
showsSearchResultsButton | 検索結果ボタンを表示するかどうか |
showsCancelButton | キャンセルボタンを表示するかどうか |
showsBookmarkButton | ブックマークボタンを表示するかどうか |
prompt | 検索バーの上のタイトル |
placeholder | 検索欄のプレースホルダー |
isSearchResultsButtonSelected | 検索結果ボタンが選択されているかどうか |
inputAssistantItem | キーボードのショートカットバーの構成に使用する入力アシスタント iPhoneまたはiPod Touchでは使用できない |
tintColor | カーソル、キャンセルボタンの色 |
barTintColor | SearchBarの背景色 barStyleを黒にしてもこちらが優先される |
searchBarStyle | .minimal:背景色が効かないtextfieldが半透明なスタイル .prominent:検索バーの背景は半透明で、検索フィールドは不透明なスタイル .default = prominent |
isTranslucent | 検索バーを半透明にするかどうか iOS 6以前では、デフォルトはfalse barStyleがUIBarStyleBlackTranslucentに設定されている場合は常にtrue |
scopeButtonTitles | 検索バーの下に表示するスコープバーのタイトル |
selectedScopeButtonIndex | 選択中のスコープバーのインデックス |
showsScopeBar | スコープバーを表示するかどうか |
inputAccessoryView | 検索バーに対するキーボードの上に表示するView |
backgroundImage | 検索バーの背景画像 |
scopeBarBackgroundImage | スコープバーの背景画像 |
searchFieldBackgroundPositionAdjustment | 検索バーの検索テキストフィールドの背景のオフセット |
searchTextPositionAdjustment | 検索テキストフィールドの背景内のテキストのオフセット |
searchTextField | 検索テキストフィールド iOS13からのみ |
プロパティは上記になりますが、関数については記載していないので時間があれば追記していこうと思います。
実際の表示動作を検証
では、上記のプロパティをいじってみてます。
1つずつ載せるとあれなので、ある程度設定して表示確認を行います。
まずはStoryboardで作る
動作確認用なので凝ったつくりはせず、シンプルにStoryboard上に設置したUIVIewControllerにUISearchBarを追加します。
そして念のためVIewの中央にはテキストが検索実行されたことがわかるようにUILabelを配置します。
こんな感じです。

ViewController側でSearchBarのプロパティをカスタマイズしていく
まずは簡単なプロパティで設定してみます。
1 2 3 4 5 6 |
searchBar.barStyle = .black searchBar.text = "test" searchBar.showsSearchResultsButton = true searchBar.prompt = "test1" searchBar.tintColor = .red searchBar.showsCancelButton = true |
ちなみにStoryboard上ではSearchBarの設定は変更していないので上記以外の項目はデフォルト設定です。
このようになりました。

searchBar.tintColor = .redに設定しているので本来はキャンセルボタンも赤色になっているはずですが、起動時にはなっていませんでした。
そこで検索フィールドをタップしてみるとキャンセルボタンが赤色になりました。
試しにテキストフィールドのフォーカス(FirstResponder)を解除してみると再度色は赤色から薄いグレーになりました。
内部でフォーカスのStateが変わったら薄く色を戻しているようです。
続きてこのように設定してみます。
1 2 3 4 5 6 7 8 9 10 11 |
searchBar.text = "test" searchBar.showsSearchResultsButton = true searchBar.prompt = "test1" searchBar.tintColor = .red searchBar.showsCancelButton = true searchBar.barTintColor = .green searchBar.scopeButtonTitles = ["test","tset","dfdf"] searchBar.showsScopeBar = true let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 60)) view.backgroundColor = .red searchBar.inputAccessoryView = view |
scopeButtonTitlesを設定してshowsScopeBarを表示すると以下のようになります。
セグメントを自前実装する必要がなくなるのでこれは結構便利かもしれません。
inputAccessoryViewは恐らくsearchTextFieldのinputAccessoryViewにバインドされているようです。

簡単なプロパティの設定はこのくらいにしようかと思います。
UISearchBarに関する個人的つまずきポイントと対処法(暫定版)
簡単なUIなら特に問題はないんですよね。
標準のそのままで特に変更なく使う場合気にしないでいいので比較的さくっと実装できます。
では、左の虫眼鏡マークをなくして、テキストフィールドを角丸にして、影をつけてなど細かな変更を加える場合、意外とうまくいかず僕は苦戦しました。
なのでみなさんが同じところでつまずいた場合に少しでも役立つようにつまずきポイントとその解決法を残していこうと思います。
ちなみに、正しい解決法でない可能性がありますので、ご注意ください。
もし他に解決案あるよという方いましたら、Twitterなどでご連絡いただけると嬉しいです。
基本的にiOS12以下とiOS13以上ではSearchBarの設定した内容の表示に差異が出る
これは開発していて気付いたのですが、iOS13ではSearchKit、つまりUISearchBarの仕様が結構変わっているようです。
このタイミングで結構中身が変更されたのかもしれません。。
_searchFieldへのアクセスについてもiOS 13からはできなくなっています(クラッシュします)
また、iOS13から以下のあたりが実装されています。
- Cancel Button
- Scope Bar
- UISearchTextField
- Search Results Controller
- Search Tokens
iOS13で同じように設定した内容でiOS12で表示してみるとちょっとずれていたり、設定が反映されていないということがありましたので
iOS12をサポートしている場合は、この辺りの表示確認はこまめに行った方が良いかと思います。
以下で書いていくのはそういう系のつまずきポイントのお話です。。
searchBarStyleによって背景色が反映されない!
背景色の変更なんて超簡単でしょ?
UIVIew.backgroundColor的なノリでやるかbarTintColorで変更するだけじゃん。
というレベルの話ではありますが、実は、UISearchBarにあるsearchBarStyleによって上記だけでは設定が反映されなパターンがあります。
searchBar.searchBarStyle = .prominentの場合
.defaultはprominentなので何もしていなければ、これが設定されています。
この場合は、barTintColorを設定してもらえれば反映します。
searchBar.searchBarStyle = .minimalの場合
.minimalに設定するとbarTintColorで設定した内容が反映されません。
上で設定していた内容にプラスしてsearchBarStypeをminimalに変更してみます。
1 2 3 4 5 6 7 8 9 10 11 12 |
searchBar.text = "test" searchBar.showsSearchResultsButton = true searchBar.prompt = "test1" searchBar.tintColor = .red searchBar.showsCancelButton = true searchBar.barTintColor = .green searchBar.scopeButtonTitles = ["test","tset","dfdf"] searchBar.showsScopeBar = true let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 60)) view.backgroundColor = .red searchBar.inputAccessoryView = view searchBar.searchBarStyle = .minimal |
するとこのようになります。
(中央のラベルは確認用で追加しているものなので無視してください。)

SearchBarの背景は白になってしまい、promptが緑になり、BarTintColorが背景色の変更として機能しなくなりました。
minimalに設定するとSearchBarの内部構造がprominentと変わってしまい、設定が反映されません。
minimalの状態で背景色を変更する場合は以下のように設定すると解決します。
1 |
searchBar.backgroundColor = .green |
SearchBarの上下に線が表示される
searchBar.searchBarStyle = .minimalでは、このボーダーラインは表示されなくなりますので、minimalに設定するか
1 |
searchBar.backgroundImage = UIImage() |
を設定すると消えます。
左側の虫眼鏡マークを非表示にしたい
iOS13以上
1 |
searchBar.searchTextField.leftView?.isHidden = true |
iOS12以下
1 2 3 |
if let searchField = (searchBar.value(forKey: "_searchField") as? UITextField) { searchField.leftView?.isHidden = true } |
以下のようにextension出だしわけておくと便利です。
1 2 3 4 5 6 7 8 9 |
extension UISearchBar { var textField: UITextField? { if #available(iOS 13.0, *) { return self.searchTextField } else { return self.value(forKey: "_searchField") as? UITextField } } } |
虫眼鏡マークを非表示にするとテキスト入力開始地点は変わらないので左の余白を調節したい
leftView自体を消しましょう。
1 |
searchField.leftView = nil |
もしくは、以下のようにしてテキスト位置を調節して対応できます。
1 |
searchBar.searchTextPositionAdjustment = .init(horizontal: -20, vertical: 0) |
キャンセルボタンをフォーカスしている時のみ表示したい
キャンセルボタンはtextFieldにデフォルトであるクリアボタンと違って、そのように融通が効かないので手動で表示非表示を設定してあげます。
テキスト入力タイミングをハンドリングして表示に設定します。
1 2 3 4 5 6 7 |
extension HogeViewController: UISearchBarDelegate { func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { if !searchBar.showsCancelButton { searchBar.showsCancelButton = true } } } |
検索のタイミングや、キーボードが非表示になるタイミングで再度showsCancelButton = falseに戻します。
ここで注意ですが、
UISearchBarのキャンセルボタンをカスタマイズのところで記載していますが、キャンセルボタンをvalue(forKey:)もしくはfor文で取得して、そのUIButton自体をカスタマイズしていた場合は、showsCancelButtonをtrueにするタイミングで再設定する必要があります。
クリアボタンを常に表示させたい
searchBarのクリアボタンはtextfiledのプロパティなので、searchTextFieldもしくは、iOS12以下は”_searchField”などで取得したUITextFieldで設定します。
1 |
searchTextField.clearButtonMode = .always |
非表示の場合は、.neverにします。
UISearchBarのキャンセルボタンをカスタマイズ
何パターンかやり方がありますが、UISearchBarのプロパティにはキャンセルボタンへのgetter,setterがありません。
その為、全体的にappearanceで設定するか、value(forKey:)もしくはfor文でsubviewを取ってくるかという方法が必要となります。
UIBarButtonItem.appearanceを使う
1 |
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Test" |
これでタイトルの変更は可能ですが、他のViewでSearchBarを使っているところにもこの設定が反映されてしまうので文言がアプリ全体で統一していない場合は、あまりおすすめしませんが、正式な設定方法はこちらなようです。
value(forKey:)でのアクセス
1 |
searchBar.value(forKey: "cancelButton") as? UIButton |
UISearchBar内部の非公開APIとなっているキャンセルボタンもしくは、その中のテキストフィールドを直接取ってきて強引に設定するやり方です。
StackOverflowなどでは頻繁にこちらの例がでていますが、推奨される方法ではありません。
UISearchBarの仕様が変わりプロパティ名が変更された場合は、アクセスしようとするとクラッシュしますし、非公開なのでおすすめではありません。
1 2 |
cancelButton?.setTitle("fldksf", for: .normal) cancelButton?.tintColor = .purple |
さらにこのようにして設定した後、searchBar.showsCancelButton = false
設定すると変更した内容はリセットされます。
詳しくは調べていませんが、恐らくsearchBarのcancelButtonは表示非表示のタイミングで毎回生成されているのか、もしくは、そのタイミングでsearchBar側で設定されていた値に再設定されているからではないかと思います。
なのでこの方法かつ、キャンセルボタン表示非表示を切り替える場合は、都度再設定が必要となります。
for文でsubviewを取り出す
これもお勧めしません。
やり方によりますが、subviewの階層が変わった場合、取り出せなくなる恐れもあります。value(forKey:)比べたらクラッシュのリスクは少ないイメージかと。。
iOS13とiOS12以下ではsubviewsの階層が違うのか同じ処理で取り出せなかった為、考えた結果、出しわけとなりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
extension UISearchBar { var cancelButton: UIButton? { if #available(iOS 13.0, *) { for subview in subviews { for secondSubview in subview.subviews { for thrdSubview in secondSubview.subviews where thrdSubview.isKind(of: UIButton.self) { return thrdSubview as? UIButton } } } } else { return self.value(forKey: "_cancelButton") as? UIButton } return nil } } |
今回はここまで
書きたい内容が結構あったので、まだ全て書ききれていません。
空き時間見てまた更新しようと思います。
ご覧いただきありがとうございました。
久しぶりの記事はSwiftについて書いていこうと思います。