schoo で iOS の授業をしてきました [検索欄を追加してアプリを完成させる編]

スクリーンショット 2015-11-02 16.10.47

はじめに

前回に引き続き、9月にオンライン授業サイト schoo(スクー) 様にて授業させていただいた技術について、補足・解説をしていきます。

授業が録画されているものが授業サイト上にあります(リンクは後述)。より理解を深めたい方は、こちらをご覧頂ければと存じます。

謝辞

スクー様では、企画段階から事前の授業資料のフィードバック、そして放送の本番に至るまで、様々な方にお世話になりました。またクラスメソッド社内ではコードや授業資料のレビュー、さらにプレ授業を社内でやらせていただきフィードバック頂くなど、お世話になりました。ありがとうございました!

リンク

授業ページ

GitHub

なお、最新の Xcode で動作するよう、今回、プログラム内容を一部修正しております。

ゴール

※ 以下、3回目の授業での授業の内容を補完する形で、書いていきたいと思います。

検索ワードを入力すると、曲情報が一覧形式で表示されるアプリを作ることが、シリーズを通してのゴールになります。

今回の完成品

今回の完成品はコレです。Web API で取得した曲データを、Table View を使って、綺麗に一覧表形式で表示する所までやってみます。

コードは、こちらになります。

SongsViewer/SearchSongsTableViewController.swift at master · cm-ttamiya/SongsViewer · GitHub

今回は、内容が多岐に渡りますので、解説にあたってはプログラム一部を省略します。コチラのプログラムを適宜コピーするなどして、動作を確認してみてください。

Search Bar とは

iOSアプリ開発実践(3)2015-09-07.009

  • 検索機能を実現するための部品
  • 検索ワードの入力欄を持つ
  • 検索・キャンセルボタンが押された時など、プログラム側にそうしたイベントを伝える機能を持つ

Search Bar を使った処理の流れ

iOSアプリ開発実践(3)2015-09-07.010

ユーザーが Search Bar に検索ワードを入力し、検索ボタンをタップした際の処理の流れを考えてみます。

ユーザーが検索ワードを入力

検索ボタンが押された時点で、その情報が プログラムの側に伝わります。

検索ワードを使って処理

プログラム側では、Search Bar の側から渡ってきた検索ワードを使い、Web API を使った曲の検索を行います。

その先は第2回と同じ流れです。Table View を使った表示内容の更新処理を行います。

前回プログラム・Storyboard上でのコピーと Search Bar 追加

前回のプログラムをコピーし、Search Bar を追加しましょう。

Storyboard

  • 前回作成した Table View Controller をコピー
    • 前回作成した Table View Controller (Custom ClassとしてSongsTableViewControllerを指定したもの)をコピーします。
    • Custom ClassSearchSongsTableViewController に変更します。
    • initial view controller として設定します。
  • Search Bar を追加
    • 上記 Table View の一番上の方に、Search Bar を追加します。
  • Navigation Controller の追加
    • Table View Controller を選択し、Xcode メニューの Editor > Embed In > Navigation Controller を選択すると、ウインドウの上にスペースが生まれ、見た目が良くなります。コチラは、今回のプロジェクトを進めるに当たり、操作しなくても構いません。

コーディング

  • swift ファイルの新規作成
    • SearchSongsTableViewController.swiftUITableViewControllerのサブクラスとして作成します。
  • 前回のプログラムのコピー
    • 前回作成したSongsTableViewController.swiftの中身をそのままコピーして貼り付けます。
  • プログラムの一部修正
    • class SongsTableViewControllerとなっているところを、class SearchSongsTableViewControllerに書き換えます。
  • Storyboard とコードのひも付け iOSアプリ開発実践(3)2015-09-07.021

    • プログラム側で Search Bar を使用できるように、図のように Storyboard 上の Search Bar からコード上にドラッグ&ドロップします。 iOSアプリ開発実践(3)2015-09-07.022
    • その後、ポップアップが表示されるので、ConnectionOutletTypeUISearchBarになっていることを確認し、プログラム側から Search Bar を扱うために使う名前を定義します。

Search Bar 用にプログラムの修正・追加

iOSアプリ開発実践(3)2015-09-07.024

最初に、Search Bar と プログラムがメッセージのやり取りをするために、Storyboard 上で必要な操作があります。図のように、Search Bar と SearchSongsTableViewControllerをひも付けます。今回も、前回の Table View の時と同じく、プログラムと Storyboard がメッセージを送り合いながら協調して動きます。この実現にはデリゲートという仕組みを使っているのですが、実現のためには、このひも付け操作が必要になります。

ここから先は、追加した Search Bar と Web API を使った検索が行えるように、コードの修正・追加をしていきます。

デリゲート

class SongsTableViewController: UITableViewController {

を、以下のように書き換えます。

class SongsTableViewController: UITableViewController, UISearchBarDelegate {

これにより、Search Bar 側から、プログラム内の必要な機能を呼び出してもらうことが出来るようになります。これも delegという仕組みを実現するための修正の一貫です。

デリゲートメソッド

上記の仕組みで呼び出されるメソッド(機能のかたまり)のことを、デリゲートメソッドと呼びます。

func searchBarSearchButtonClicked(searchBar: UISearchBar) {
}

これは、検索ボタンが押された時に呼び出されるメソッドです。

func searchBarSearchButtonClicked(searchBar: UISearchBar) {
	print("検索ボタンが押されました")
}

細かい内容を書いていく前に、まずは上記のように、書いてみましょう。コレを、プログラムのおしりの方に追記してみましょう。追記する場所を間違えるとうまく動かない場合もあります。その場合は、GitHub にアップされているサンプルを見てみて下さい。

上記のコードを実行してみましょう。

Search Bar をタップして出てきたキーボード上の、青い検索ボタンをタップすると、Xcode 上に「検索ボタンが押されました」と表示されるはずです。

fetchSongs メソッドの修正

引数

前回とは異なり、fetchSongs メソッドでは、Search Bar に入力された、毎回異なる語句を検索可能にします。そのために、fetchSongsメソッドに、検索ワードを受け取るための口を設けます。

fetchSongsメソッドの後には、()がついていました。この()の中に、語句を受け取るための記述を行います。

private func fetchSongs() {

これを、以下のように書き換えます。

private func fetchSongs(term:String) {

term:String のような、メソッド側で受け取って使うための値や語句のことを、引数(ひきすう)と呼びます。 ここで、termは任意の名前、Stringは、受け取る値の型(ここでは文字列を扱うことのできるString型)です。

fetchSongs(term:String) 内部の修正

let urlString = "https://itunes.apple.com/search?term=Ryuichi+Sakamoto&entity=musicTrack&limit=3&lang=ja_jp&country=JP"

ここで、term=の後にくる検索語句を、引数で得た語句を扱う処理に変更してみます。

let urlString = "https://itunes.apple.com/search?term=\(term)&entity=musicTrack&lang=ja_jp&country=JP"

term=(term)としました。()を使うことで、カッコの中に入っている値を、文字列として生成してくれます。引数termの中身がRyuichi+Sakamotoであれば、term=Ryuichi+Sakamotoとなります。

ついでに、&limit=3を消し、Web API で取得する曲数の制限を無くすことで、たくさんの曲情報を表示できるようにしています。

searchBarSearchButtonClicked

iOSアプリ開発実践(3)2015-09-07.029

デリゲートメソッドである、searchBarSearchButtonClicked メソッドを書いていきます。

func searchBarSearchButtonClicked(searchBar: UISearchBar) {
    self.songs.removeAll(keepCapacity: false)
    self.fetchSongs(string)
}

コレにより、Search Bar 上で入力された語句を使って、Web API をつかって検索結果を取得し、Table View 上に曲名が表示できるようになります。

実際にこの状態で実行し、動作を試すことが出来ます。

ただし、ここで日本語で検索をしようとすると、途中でエラーが表示され、アプリの動作が止まってしまいます。

完成度を上げる

ここまでの作成内容により、検索欄に英文検索ワードを入力し、曲一覧を表示することが出来ます。しかし、日本語で検索しようとするとアプリがクラッシュするなど、完成度は今一歩です。ここから先は、以下の処理を追加し、アプリの完成度を高めていきたいと思います。

  • 日本語で検索するための文字列変換
  • 画像表示
  • 検索ボタンを押した後にキーボードが隠れる処理(結果表示領域を広げる)
  • 検索ボタンを押してから実際に曲情報が表示されるまでの間に、クルクル回るインジケータを表示

日本語で検索するための文字列変換

iOSアプリ開発実践(3)2015-09-07.034

Web API で検索するときには、URLを使ってきました。このURL、日本語を扱うには、%で始まる文字の集まりに変換する必要があります。この変換する処理のことを「パーセントエンコーディング」と呼びます。例えば、スピッツであれば、例えば%E3%82%B9%E3%83%94%E3%83%83%E3%83%84となります。

実際にパーセントエンコーディングする処理を書いていきましょう。

呼び出し先

private func getEncodedString(original: String) -> String
{
    let encodedString = original.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
    return encodedString
}

getEncodedString メソッドを作成しました。ここで、引数は String型の original

そして、-> Stringは、このメソッドは別のメソッドから呼び出される事になるわけですが、その呼び出し元に値を返す時に使う型が String であることを示しています。

変換前の文字列originalをパーセントエンコードし、encodedStringに代入した後、return encodedStringにより、呼び出し元に渡しています。

呼び出し元

func searchBarSearchButtonClicked(searchBar: UISearchBar) {
    self.songs.removeAll(keepCapacity: false)
    let string = getEncodedString(searchBar.text!)
    self.fetchSongs(string)
}

先ほどまでは、検索ボタンが押された時のデリゲートメソッドは、このように書かれていました。Search Bar 内の文字列をそのまま fetchSongs メソッドに渡していました。

それを、getEncodedStringを使って変換した文字列を使った検索に書き換えます。

画像表示

fetchSongsメソッドのなかで、画像データを読み込み、cellForRowAtIndexPathの名前がついたメソッド中で、Table View Controller にその画像データがセルの中身として表示されるよう処理を書いていきます。

var artworks = [UIImage]()

プロパティとして、UIImage型の配列 artworksを定義します。

iOSアプリ開発実践(3)2015-09-07.039

for song in self.songs {
    let url = song["artworkUrl30"] as! String
    let imageData = NSData(contentsOfURL: NSURL(string: url)!)
    let image = UIImage(data: imageData!)
    self.artworks.append(image!)
}

fetchSongsメソッド中で、画像データを読み込む処理を行っています。Web API で取得した 曲のジャケット写真URLを元に画像データを取得し、artworksに代入しています。

iOSアプリ開発実践(3)2015-09-07.040

cell.imageView?.image = artworks[indexPath.row]

cellForRowAtIndexPathの名前がついたメソッドのなかで、画像表示処理を書いていきます。配列[artworks] の中には、songsと同じ順で曲の画像データが入っていますので、上記の処理により、曲に対応する画像が表示されます。

検索ボタンを押した後にキーボードが隠れる処理(結果表示領域を広げる)

self.searchBar.resignFirstResponder()

searchBarSearchButtonClicked と、searchBarCancelButtonClickedの中で、この処理を呼ぶことで、検索ボタンをタップした時と、キャンセルボタンをタップした時にキーボードが隠れ、曲の一覧表示領域を広げることができます。

検索ボタンを押してから実際に曲情報が表示されるまでの間に、クルクル回るインジケータを表示

iOSアプリ開発実践(3)2015-09-07.042

検索ボタンが押されてから、実際にデータが表示されるまでの間にはタイムラグがあります。この「空白の時間」には、ここまでのところ何も起こらないわけですから、「アプリが固まってしまった」と思う人もいるかもしれません。

UIApplication.sharedApplication().networkActivityIndicatorVisible = true

この処理で、画面左上の方にクルクル回るインジケータを表示させることが出来ます。

Web API でデータを取得する直前にこの処理を書いてあげることで、「データを今読み込んでいるんだな」とユーザーが知る手助けになります。

UIApplication.sharedApplication().networkActivityIndicatorVisible = false

このコードで、インジケータが表示されなくなります。データ取得が完了した場所で書くとよいでしょう。

アプリ完成

ここまでで、曲検索アプリが完成しました!

iOSアプリ開発実践(3)2015-09-07.043

今回のまとめ

今回は、主に以下について学びました。

  • Search Bar
  • 入力した検索ワードを使って Web API で情報を取得
  • 画像を取得
  • Table View に画像を表示

3回を振り返る

3回を通じて、主に以下について学んできました。わからない部分は、スクー様の授業サイトや、本シリーズ、そして本ブログ Developers.IO や ネット上の情報をリサーチされることをオススメします!

  • Web API
  • JSON
  • データの変換・抽出
  • Table View
  • Search Bar

まとめ

IMG_5832

スクー様での授業を通して、情報を分かりやすく伝えるコツや、映像メディアの力などを垣間見ることが出来ました。まだまだ未熟者ではありますが、積極的にチャレンジを続けながら、技術を学び、仕事に活かし、その楽しさをより多くの方々に伝えることができたらと思っています。

これでこのシリーズは終わりとなります。

読んでいただきありがとうございました。