【2020年 保存版】UISearchBarのカスタマイズ

kazy
どうもご無沙汰してます。kazyです。
久しぶりの記事はSwiftについて書いていこうと思います。

iOSアプリで検索バーを実装する方法

今回は、iOSで検索バーを実装するにあたって個人的につまずいた点が結構あったので、備忘録兼ねて、そのカスタマイズ方法について書いていきます。

検索バーを実装する方法は主に以下の2つあります。

・UISearchBarを使う方法
・自力実装する方法

大抵の場合は、UIKitにある標準のUISearchBarで事足りるかと思います。

僕も普通に使うのは大抵こちらです。

UISearchBarはその名の通り、Delegateやプロパティが検索用として設定できるので、ほぼこちらを使う方が大半だと思います。

もう一つの自力実装するやり方は、UITextFieldを実装するやり方や、あまりないですが、UITextViewを使ったやり方があるかと思います。

検索を実装する際は、大抵の場合で1行表示のみかつトップに設置するのでUITextFieldを使用することは滅多にないかと。

さっそく見ていきましょう!

UISearchBarの基本的なプロパティ

まずUISearchBarの基本的なプロパティから見ていきます。

barStyleSearchBarの背景色のスタイル
.default:灰色
.black:黒色
delegateコールバック通知用
text検索入力欄のテキスト
showsSearchResultsButton検索結果ボタンを表示するかどうか
showsCancelButtonキャンセルボタンを表示するかどうか
showsBookmarkButtonブックマークボタンを表示するかどうか
prompt検索バーの上のタイトル
placeholder検索欄のプレースホルダー
isSearchResultsButtonSelected検索結果ボタンが選択されているかどうか
inputAssistantItemキーボードのショートカットバーの構成に使用する入力アシスタント
iPhoneまたはiPod Touchでは使用できない
tintColorカーソル、キャンセルボタンの色
barTintColorSearchBarの背景色
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のプロパティをカスタマイズしていく

まずは簡単なプロパティで設定してみます。

ちなみにStoryboard上ではSearchBarの設定は変更していないので上記以外の項目はデフォルト設定です。

このようになりました。

searchBar.tintColor = .redに設定しているので本来はキャンセルボタンも赤色になっているはずですが、起動時にはなっていませんでした。

そこで検索フィールドをタップしてみるとキャンセルボタンが赤色になりました。

試しにテキストフィールドのフォーカス(FirstResponder)を解除してみると再度色は赤色から薄いグレーになりました。

内部でフォーカスのStateが変わったら薄く色を戻しているようです。

続きてこのように設定してみます。

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に変更してみます。

するとこのようになります。
(中央のラベルは確認用で追加しているものなので無視してください。)

SearchBarの背景は白になってしまい、promptが緑になり、BarTintColorが背景色の変更として機能しなくなりました。

minimalに設定するとSearchBarの内部構造がprominentと変わってしまい、設定が反映されません。

minimalの状態で背景色を変更する場合は以下のように設定すると解決します。

SearchBarの上下に線が表示される

searchBar.searchBarStyle = .minimalでは、このボーダーラインは表示されなくなりますので、minimalに設定するか

を設定すると消えます。

左側の虫眼鏡マークを非表示にしたい

iOS13以上

iOS12以下

以下のようにextension出だしわけておくと便利です。

虫眼鏡マークを非表示にするとテキスト入力開始地点は変わらないので左の余白を調節したい

leftView自体を消しましょう。

もしくは、以下のようにしてテキスト位置を調節して対応できます。

キャンセルボタンをフォーカスしている時のみ表示したい

キャンセルボタンはtextFieldにデフォルトであるクリアボタンと違って、そのように融通が効かないので手動で表示非表示を設定してあげます。

テキスト入力タイミングをハンドリングして表示に設定します。

検索のタイミングや、キーボードが非表示になるタイミングで再度showsCancelButton = falseに戻します。

ここで注意ですが、
UISearchBarのキャンセルボタンをカスタマイズのところで記載していますが、キャンセルボタンをvalue(forKey:)もしくはfor文で取得して、そのUIButton自体をカスタマイズしていた場合は、showsCancelButtonをtrueにするタイミングで再設定する必要があります。

クリアボタンを常に表示させたい

searchBarのクリアボタンはtextfiledのプロパティなので、searchTextFieldもしくは、iOS12以下は”_searchField”などで取得したUITextFieldで設定します。

非表示の場合は、.neverにします。

UISearchBarのキャンセルボタンをカスタマイズ

何パターンかやり方がありますが、UISearchBarのプロパティにはキャンセルボタンへのgetter,setterがありません。
その為、全体的にappearanceで設定するか、value(forKey:)もしくはfor文でsubviewを取ってくるかという方法が必要となります。

UIBarButtonItem.appearanceを使う

これでタイトルの変更は可能ですが、他のViewでSearchBarを使っているところにもこの設定が反映されてしまうので文言がアプリ全体で統一していない場合は、あまりおすすめしませんが、正式な設定方法はこちらなようです。

value(forKey:)でのアクセス

UISearchBar内部の非公開APIとなっているキャンセルボタンもしくは、その中のテキストフィールドを直接取ってきて強引に設定するやり方です。

StackOverflowなどでは頻繁にこちらの例がでていますが、推奨される方法ではありません。

UISearchBarの仕様が変わりプロパティ名が変更された場合は、アクセスしようとするとクラッシュしますし、非公開なのでおすすめではありません。

さらにこのようにして設定した後、searchBar.showsCancelButton = false

設定すると変更した内容はリセットされます。

詳しくは調べていませんが、恐らくsearchBarのcancelButtonは表示非表示のタイミングで毎回生成されているのか、もしくは、そのタイミングでsearchBar側で設定されていた値に再設定されているからではないかと思います。

なのでこの方法かつ、キャンセルボタン表示非表示を切り替える場合は、都度再設定が必要となります。

for文でsubviewを取り出す

これもお勧めしません。

やり方によりますが、subviewの階層が変わった場合、取り出せなくなる恐れもあります。value(forKey:)比べたらクラッシュのリスクは少ないイメージかと。。

iOS13とiOS12以下ではsubviewsの階層が違うのか同じ処理で取り出せなかった為、考えた結果、出しわけとなりました。

今回はここまで

書きたい内容が結構あったので、まだ全て書ききれていません。

空き時間見てまた更新しようと思います。

ご覧いただきありがとうございました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA