はじめてのiOSアプリケーション開発がひと段落してきたので、基本的な開発方法を一通りまとめておきます。iOSに関する記事はxxViewの使い方、みたいな記事が多くて意外とチュートリアル的なものがなかったので、これから開発を始める方の参考になればと思います。
環境
- Yosemite(10.10.1)
- Xcode(6.1.1)
- iOS8/Swiftにて開発
iOSアプリのアーキテクチャ
公式ドキュメントに詳しく記載されています。この文書は開発チュートリアル的なものではなく、こうした指針に沿って開発すべき・こういうポイントに気を配るべき、というような方針について書かれたものになっています。
ここに書かれている中で重要な点は、下記点です。
- iOSアプリケーションは、MVCの構成になっている
- アプリケーションは、フォアグラウンド/バックグラウンドの二種類の状態がある
- 状態が切り替わるタイミングと、切り替わる時に何をすべきか
iOSアプリケーションは以下のようなMVC構成になっていて、View Controllerと呼ばれるControllerが、Modelからデータを取得し、Viewの描画処理を管理します。
(p23 図 2-1 iOSアプリケーションの主要オブジェクト より引用)
そして、アプリケーションは常にアクティブ(フォアグラウンド)というわけではありません。ホームボタンが押されたり他のアプリが起動された場合には、バックグラウンドに回ることになります。全体として、状態は以下のように遷移します。
(p29 図 2-3 iOSアプリケーションの状態遷移 より引用)
ガイドの中では、各状態の特性や切り替わる時に何をした方が良いのか(例えばデータを保存しておくなど)について、詳しく述べられています。
これら基本的な事項以外にも、パフォーマンスのためのTipsなども記載されているので一旦目を通しておくと良いと思います。
そして、何と言ってもアプリケーションの中核となるのはControllerであるView ControllerとViewになります。これらについての詳細なドキュメントも用意されているので、一旦目を通しておく良いです。
iOS View Controller プログラミングガイド
ざっくり言うと、View ControllerとViewであるWindowとViewの関係は以下のようになっています。
- Window
画面スクリーンと同義。多くの場合Windowを表すUIWindowは一つであり、この中でViewが切り替わっていくイメージ。 - View
ViewはWindow内に描画されるが、そのタイミングや描画に必要なリソースについて把握していない。これはViewControllerが管理することになる。 - View Controller
ViewをWindowに表示するという基本的な役割は、Content View ControllerというView Controllerによって行われる。これ以外に、複数のView Controllerをまとめてその表示順などを管理するContainer View Controllerが存在する。Container View Controller自体は表示すべきViewは持っておらず、子のView Controllerを表示する。
後の開発の項で出てきますが、Viewを表示するための基本的なUIViewControllerやUITableViewControllerなどがContent View Controller、ナビゲーションを管理するためのNavigationViewController、タブ管理のためのUITabBarControllerなどがContainer View Controllerになります。
上記のドキュメントを読むことで、iOSアプリケーションがどういう仕組みで動いているのかがなんとなくつかめると思います。
iOSアプリの開発
ここからは実際に開発を行っていきます。
iOSアプリケーションの開発を行う場合、開発環境はXcodeが第一となります(それ以外でできるのかは定かでないです)。このXcodeには画面遷移をGUIで組み立てられるStoryboardという仕組みが付属しており、これを利用するとかなり簡単にアプリを作ることができるためこれを利用します。なお、開発言語はもちろんSwiftで。
ここに素晴らしいチュートリアルがあるので、まずはこの通りに進めていきます。
Swift Tutorial: Building an iOS application
英語なのは仕方がないとして、このチュートリアルは探し回った中で一番良かったです。
まずクセの強いXcodeの使い方がきちんと動画付きで説明されている点、またSwiftの言語特性や記法についても言及がある点、そしてなにより開発の一通りの流れが網羅されている点、何れも他の記事にはないポイントでした。
このチュートリアルをこなすと、以下のポイントが理解できます。
- 画面の作り方(Storyboardへの追加、UIViewControllerのカスタマイズ方法)
- 画面のレイアウト方法(Constraintsを利用した画面コンポーネントのAuto Layout)
- 画面コンポーネント(ボタンなど)からのイベントハンドリング方法
- 画面遷移の方法(segue)
- 画面遷移時のパラメーターの受け渡し方法(相手先ViewControllerのプロパティへ値セット)
- Modelデータの保持方法(Singleton)
補足として、何点か。
- 画面のパラメータ受け渡しでプロパティのdidSetが利用されていますが、プロパティが設定された瞬間は画面コンポーネントがなく結果として処理が通らないことが多いため、この処理はなくて良いと思います。どのみちviewDidLoadで処理されるので(受け渡し方法はこちらも詳しいです)。
- segueでModalを選択したのになぜ画面全体を覆っているんだ?Modal Dialogぽくなるんじゃないの?と思った方もいると思います。これは仕様で、ポップアップのようにしたい場合はポップアップのViewを別途配置し背景を透明にする必要があります。詳細はこちらを参照ください。
これであとはxxViewの使い方などを調べることで、自由に開発していくことができるようになります。
付録
Swiftの文法などで困った時には
Swift: A Quick Reference Guide
日付計算で困った時には
NSDateExtension.swift
いろんな情報に翻弄されましたが、最終的に良しとされた処理をまとめたExtensionです、これを入れていただいてもいいですし、処理の参考にしていただいても良いです。
ジェスチャーを追加したい時には
一応Storyboard経由でもできるはずなんですが、なぜかうまく反応しないことが多かったです(やり方が悪いのか・・・)。以下に簡単な追加方法を記載しておくので、参考にしてください(viewDidLoadに実装)。
let longPressHandler: Selector = "handleLongPress:"
let longPressRecognizer: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: longPressHandler)
view.addGestureRecognizer(longPressRecognizer)
@IBAction func handleLongPress(sender: UILongPressGestureRecognizer) {
//some process
}
Selectorでジェスチャーを認識した際実行したいメソッドを指定し、それで該当のジェスチャーのRecognizerを作成しviewオブジェクトに追加する、という流れです。
子から親に通知したい
子の画面を閉じるときに親を更新したい、ということは結構あると思います。単純に親のViewControllerのメソッドを呼び出せば済むと思っていたのですがそういうわけでもないようなので、NSNotificationCenterという仕組みを使います。
親側(viewDidLoadに実装)
let refresh: Selector = "refresh:"
NSNotificationCenter.defaultCenter().removeObserver(self, name: "refresh", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: refresh, name: "refresh", object: nil)
func refresh(notification: NSNotification){
//some process
}
追加前に削除しているのは、必要ないかもしれないが念のため(全体で共通の印象を受けたので・・・)。
子側(画面を閉じるsegueを実行する前のprepareForSegueなどで実装)
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil)
TableViewをリフレッシュしたい
tableView.reloadRowsAtIndexPaths
か、tableView.reloadData
を使用する。
アニメーションをつけたい場合は、前者一択になる。ただ、表示している配列の要素の入れ替えなどがあった場合キャッシュのせいかなんなのかうまく行かない。
そのため、単純な追加/削除でアニメーションをつけたい場合は前者、そうでない複雑な変更の場合は後者を利用するのが良いと思う。