概要
以下の書籍を読み進めた際の読書メモです。
所要時間は約12時間でした。
前回読んだ本がかなりハンズオン重視で様々なアプリを次々と作っていく内容であったのに対して、こちらはより言語仕様や概念、各種機能の解説を重視した内容でした。(本書でもハンズオンでいくつかのアプリは作りますが、どちらかというと座学に近い印象)
変数・定数・配列といったかなり基本的なものからデリゲートなどの少し複雑なものまで例を挙げてしっかり解説されているので、プログラミング初心者はもちろん、プログラミング経験はあるがSwiftは初心者という人まで幅広く対応していると感じました。
前回の本と合わせて、Swiftの基礎概念とiPhoneアプリ作成の基本的な方法は一通り把握できた気がします。
書籍概要
- 書籍名:絶対に挫折しないiPhoneアプリ開発「超」入門
- 著者 :高橋京介
- 発刊日:2019/05/10
- 頁数 :399ページ
読書ノート
Playground
-
XcodeのPlayground機能を使うと、生のSwiftを書いて色々試すことが出来る
- スライダー等のUIパーツを表示することもできる
-
PlaygroundではデフォルトではUIパーツのアニメーションを確認することはできない(UISliderのつまみの移動など)
- PlaygroundSupportというフレームワークを使うとアニメーションを確認できるようになる
変数・定数
-
Swiftでは変数宣言時に初期化することが推奨されている
-
Swiftでは変数名の大文字・小文字が区別される
-
Swiftでは変数名はキャメルケースで設定するのが慣例
- アッパーキャメルケース or ローワーキャメルケース
-
Swiftでは変数や定数の初期化時に型を自動判別してくれるので明示的に型を指定する必要はない(型推論)
- もちろん明示的に型を指定することもできる
- Swiftは静的型付け言語
ラベル
- 関数宣言時にラベルを設定すると、関数呼び出し時にラベルで引数を指定できる
- 関数内で処理する際には引数名を使う
- ラベルの設定を省略した場合でも、内部的に「引数名と同じ名前のラベル」が自動的に設定される
- 関数の宣言時にラベルにアンダースコアを指定すると、関数の呼び出し時にラベルを書けなくなる
// ラベルの指定を省略した場合
func areaOfTriangle(base:Int, height:Int) {
print(base * height / 2)
}
areaOfTriangle(base:2, height:3)
// ラベルを指定した場合
func areaOfTriangle(withBase base:Int, height height:Int) {
print(base * height / 2)
}
areaOfTriangle(withBase:2, height:3)
// ラベルにアンダースコアを指定した場合
func areaOfTriangle(_ base:Int, _ height:Int) {
print(base * height / 2)
}
areaOfTriangle(2, 3)
ドキュメントアウトライン
- ドキュメントアウトライン
- View Controller Scene:1画面に含まれる要素をまとめているフォルダ
- View Controller:ViewControllerクラス。この上にUIパーツを配置して画面レイアウトを作成する。コードもここに書く
- View:UIパーツを配置するための土台
- Safe Area:時刻表示エリアなど、他のUIパーツで隠れない範囲
- First Responder:ユーザーのアクションに最初に反応するクラスやインスタンス
- Exit:Storyboardのエディタエリアに複数の画面がある場合に使用する要素
- Storyboard Entry Point:最初に表示する画面を示す要素
ビュー
-
自身が乗っているビューのことを「スーパービュー」と呼び、自身が乗せているビューのことを「サブビュー」と呼ぶ
-
モーダルビュー
- 現在の画面の上に覆いかぶさって表示される一時的な画面のこと
- 背景がグレーアウトされて下の画面は操作不可能になる
- モーダルビューを操作するクラスを「モーダルビューコントローラ」と呼ぶ
- 現在の画面の上に覆いかぶさって表示される一時的な画面のこと
UIパーツ
-
UIパーツは全てクラスとして実装されている
-
UIパーツは階層構造になっている
-
UIパーツをコードを接続する際は、右クリックまたはControlキーを押しながらパーツをエディタにD&Dする
- ドキュメントアウトラインからD&Dしても良い
UIパーツとコードの接続
-
パーツとコードの接続の種類
- Outlet:UIパーツを「プロパティ」として接続する
- パーツの見た目をコードで変更したい場合に指定する
- Action:UIパーツを「メソッド」として接続する
- パーツに対して何かしらの操作が発生した際に特定処理を実行したい場合に指定する
- Outlet:UIパーツを「プロパティ」として接続する
-
Action接続の設定値
- Event:様々な操作に応じたイベントが登録されている
- Arguments:多くの場合は「None」を選択するが、パーツそのものを引数として渡したい場合は「Sender」を選択する
- 例えば、ボタンの見た目をメソッドの中で変更したい場合などに「Sender」を選択する
- 見た目を変更するには、Action接続に加えてOutlet接続も行えば良いが、いちいちそれをやるのは面倒
- 例えば、ボタンの見た目をメソッドの中で変更したい場合などに「Sender」を選択する
-
Storageの設定値
- WeakとStrongがあるが、これらは参照の強さを表している
Auto Layout
- Auto Layout
- StoryboardにUIパーツを配置する際、適切な位置に配置することで、Xcodeで自動的にAutoLayoutの制約を設定してくれる
- もちろん自分で細かく制約を指定することもできる
- 各メニューの説明
- Update Frames:制約に沿っていないUIパーツを自動的に制約に沿った場所に再配置する(サイズ変更含む)
- Embed in Stack:複数のUIパーツを選択した状態でクリックすると、水平または垂直方向に整列してくれる(方向は自動)
- Align:各UIパーツの整列に関する制約を設定する
- Add New Constraints:UIパーツのサイズや他パーツとの距離といった、間隔に関する制約を設定する
- Resolve Auto Layout Issues:まだ設定されていない制約が自動的に追加される
- 通常はUIパーツの配置後に「Add Missing Constraints」を選択すれば事足りる
- UIパーツが赤色の線で囲まれている場合、「制約設定が不十分である」ことを示している(垂直方向しか制約が設定されていない等)
- スーパービューではなく、同ビュー上の別UIパーツとの位置関係の制約を設定することも出来る
- Controlキーを押しながら一方のUIパーツをもう一方のパーツにD&Dする
- 現状のXcodeでは、「Add Missing Constraints」を選択した際に余計な制約が付加されるので、手動で削除する
- StoryboardにUIパーツを配置する際、適切な位置に配置することで、Xcodeで自動的にAutoLayoutの制約を設定してくれる
継承
- Swiftでの継承の記述方法
-
class (子クラス): (親クラス) { ... }
- 例:
class ViewController: UIViewController { ... }
- 例:
-
ビューコントローラ
-
ビューコントローラ
- ビュー:テレビゲームで言うところの「ディスプレイ」のようなもの
- 実体はビューコントローラー(ViewController)のプロパティとして実装されている
- コントローラ:テレビゲームで言うところのコントローラ(そのまま)
- ビューコントローラは一つの画面に対して一つが基本
- 両者が分離されていることで、どちらか片方を変えてももう片方はそのまま使えるというメリットが生まれる
- ビュー:テレビゲームで言うところの「ディスプレイ」のようなもの
-
UIViewControllerクラスのviewプロパティにコードでUIパーツを追加することも可能だが、Storyboardを使う方がベター
-
ViewControllerクラスは、iOSが裏側で使っているので、使用するためのコードを書く必要は特にない
override・super・self
-
override
- 親クラスのメソッドを子クラスで上書きする機能
- 「メソッド名・引数の型・引数の数・引数名が同じ」である必要がある
- JavaなどのOverrideと比べて、引数名も同じでなければならないので注意
-
super
- 親クラスのメソッドの「一部分だけ」を子クラスで上書きする機能
-
self
- superと対になるキーワードで、「自分自身のインスタンス」を表している
-
viewDidLoad()
- アプリの初回起動時(メモリ上にロードされた時)に実行したい処理を記述する
- 例:Storyboardで作成した画面レイアウトをコード上で微調整する場合など
- ここにデフォルトで書かれている
super.viewDidLoad()
は上述したsuperの使い方の一例
- アプリの初回起動時(メモリ上にロードされた時)に実行したい処理を記述する
オプショナル型
- オプショナル整数型
- オプショナル整数型と通常の整数型はともに数値を表す型ではあるが別物なので、そのまま計算などは出来ない
- 変数名の後に「!」を付けてオプショナル整数型から整数型の値だけを取り出せば処理可能
- 変数にnilが代入されている状態で「!」を付けると実行時にアプリがクラッシュするので注意
- 変数名の後に「!」を付けてオプショナル整数型から整数型の値だけを取り出せば処理可能
- 他の型(StringやUISliderなど)のオプショナルについても同様
- オプショナル整数型と通常の整数型はともに数値を表す型ではあるが別物なので、そのまま計算などは出来ない
// エラーになる
var age:Int? = 25 // オプショナル整数型
print(age + 1) // オプショナル整数型の変数に、整数型の値を足そうとしているのでエラーになる
// エラーにならない
var age:Int? = 25
print(age! + 1)
- オプショナル型変数の型を通常の方に戻す時に「!」を省略する書式
- 変数を初期化する際、変数名の後ろに「?」ではなく「!」を記述する
- ただし、この書き方は「変数にnilが入っていないことが保証されている」状況でしか使ってはいけない
- 変数を初期化する際、変数名の後ろに「?」ではなく「!」を記述する
// エラーにならない
var age:Int! = 25
print(age + 1)
-
変数にnilを代入する
var (変数名):(型名)? = nil
-
オプショナル型変数の宣言時、初期化しない(初期値を代入しない)場合、自動的にnilが代入される
イニシャライザ・デフォルトイニシャライザ
-
イニシャライザ
- クラスのインスタンス生成時に初期値を強制的に設定する機能
- 他言語でいうところの「コンストラクタ」のこと
- 構文:
init( (ラベル1) (引数名1):(型), (ラベル2) (引数名2):(型) ... ) { ... }
-
デフォルトイニシャライザ
- クラス内の全てのプロパティに初期値が設定されている場合にSwiftが自動的に生成してくれるイニシャライザ
- コードを書く必要はないが、内部的には自動的に
init() {}
メソッドが追加されている
- コードを書く必要はないが、内部的には自動的に
- 他言語でいうところの「デフォルトコンストラクタ」のこと
- クラス内の全てのプロパティに初期値が設定されている場合にSwiftが自動的に生成してくれるイニシャライザ
クロージャ
- クロージャ
- 文の中に直接埋め込むことが出来る命令のかたまり
// 引数と戻り値があるクロージャの例
{ (base:Int, height:Int) -> Int in
let area = base * height / 2
return area
}
プロトコル
- プロトコル
- 他言語でいうところの「インタフェース」
- 宣言方法:
protocol (プロトコル名) { ... }
- プロトコル内にはメソッドやプロパティを書くが、メソッドの中身は書かない
protocol sample { func method() }
- プロトコルの使い方
- 宣言後、クラスがプロトコルに批准(参加すること)して始めて意味を成す
class (クラス名):(プロトコル名) { ... }
- プロトコルに批准したクラスでは、批准したプロトコルで定義されているメソッドを実装する必要がある
- プロトコル宣言時に
@objc optional
を付けると、必ずしも実装しなくて良いメソッドを定義できるprotocol sample { @objc optional func method() }
- プロトコル宣言時に
- 宣言後、クラスがプロトコルに批准(参加すること)して始めて意味を成す
- 型としてのプロトコル
- 基本的にはAppleが提供しているプロトコルを使えば良く、自分でオリジナルのプロトコルを作ることはほぼない
- プロトコルには型としての機能があり、変数宣言時にプロトコルを指定すると、その変数にはそのプロトコルを批准しているクラスしか格納できなくなる
-
var (変数名):(プロトコル名) = (クラス名)()
- (変数名)には、(プロトコル名)を批准した(クラス名)しか格納できない
- 実装段階でエラーを見つけられるので、より安全なコードを書くことができる
- 複数のプロトコルを指定した場合は「&」で繋ぐ(全て批准しているクラスしか格納できなくなる)
var sampleVar:protocol1 & protocol2 ... = sampleClass()
-
- 親クラスとプロトコル
- 継承させる場合、クラス宣言時に親クラスを最初に書き、その後ろにプロトコル名を書く(複数可能)
class childClass:superClass, protocol1, protocol2 ... { ... }
- 継承させる場合、クラス宣言時に親クラスを最初に書き、その後ろにプロトコル名を書く(複数可能)
列挙体
- 列挙体(Enum)
- Swiftに用意されていない型を独自に定義するための機能
- Enum名はアッパーキャメルケース、要素名はローワーキャメルケースで書くのが慣例
- 列挙体の値の取得:
var (変数名) = (列挙体名).(要素名)
-
extension
を使うと、既存の型に新しい機能を追加することができる- また、
extension
には、「コードを機能別に分けることでコード管理をしやすくする」という役割もある
- また、
- 列挙体の定義時に「: Int」を付与すると、列挙体の各要素に自動的に整数の番号が割り振られる
public enum Sample : Int { ... }
- 各要素に割り振られた値は、
(要素名).rawValue
プロパティで取得できる
型メソッド
- 型メソッド
- 他言語でいうところの「クラスメソッド」のこと
- インスタンスを生成することなく、直接クラスから呼び出すことができるメソッドのこと(例:デバイスにカメラがあるか調べる)
- 厳密にはクラスだけでなく、列挙体や構造体でも使用できる
- インスタンスを生成した後にしか実行できないメソッドは「インスタンスメソッド」と呼ばれることもある
- クラス内のメソッドを型メソッドにする場合、メソッドの先頭に
class
を付与する- 列挙体や構造体の場合は
static
を付与する
- 列挙体や構造体の場合は
class Sample {
class func method() -> Bool {
...
}
}
Sample.method()
デリゲート
- デリゲート
-
あるクラスだけでは処理できない命令を、そのクラスの代わりに実行するクラスのこと(代理人のようなイメージ)
-
登場人物(例:弁護を行う場合)
-
被告人(Defenderクラス)
class Defender { // 弁護士は途中で変わる可能性もあるのでletではなくvarで定義 // また、弁護士を付けずに自分自身で弁護する(代理人がnilになる)可能性もあるのでオプショナルで定義 var delegate:Lawyer? }
-
弁護士(Lawyerクラス)※ここがデリゲート
class Lawyer { func defend() { print("異議あり") } }
-
-
裁判を行う場合
// Defender自身に弁護能力がなくても、代理人を介して弁護(defend)メソッドを呼び出せる let taro = Defender() taro.delegate = Lawyer() taro.delegate!.defend()
-
デリゲート(弁護士)は必ずプロトコルを批准しなければならない
protocol LawyerLicense { func defend() }
-
これを踏まえて元に登場人物のクラスを書き換える
-
被告人(Defenderクラス)
class Defender { // delegateにはLawyerLicenseプロトコルを批准したクラスしか格納できなくなる var delegate:LawyerLicense? }
-
弁護士(Lawyerクラス)※ここがデリゲート
// LawyerLicenseプロトコルを批准する class Lawyer:LawyerLicense { // プロトコルで定義されているメソッドの実装 func defend() { print("異議あり") } }
-
-
型キャスティング
- 型キャスティング
- 現在の変数の型を別の型に変換すること
- Any型の変数で受け取った値を、安全性を保つために値に適した型に変換するなど
- 型キャスティングの種類
- 階層が上のクラス(親側)の型に変換すること:アップキャスティング
- 階層が下のクラス(子側)の型に変換すること:ダウンキャスティング
- どちらの方向にキャスティングした場合でも、変数内のインスタンス自体は変わらない
- 型キャスティングは継承関係内でしか実行できない
- アップキャスティングは常に成功するが、ダウンキャスティングはそうとは限らない
- ダウンキャスティングを実行する際は
as!
演算子を使い、ダウンキャスティングであることを明示する-
as!
演算子を使ったにも関わらず不適切なダウンキャスティングを行った場合、実行時にクラッシュする
-
- ダウンキャスティングを実行する際は
Storyboard
-
アプリ起動時にどのStoryboardを表示するかは、「General → Main Interface」項目で決定する
-
1つのStoryboard内に複数の画面がある場合、最初の開く画面を「Is Initial View Controller」で設定する
テーブルビュー
- テーブルビュー関連
- indexPathクラスのインスタンスには、tableView()メソッドが現在設定を行っているセルのセクション番号と行番号が保持される
-
dequeueReusableCell()
メソッドを使うと、自動的にセルの再利用を行える - ViewControllerクラスでUITableViewDataSourceプロトコルを批准していないのに問題がない理由
- ViewControllerクラスではUITableViewControllerクラスを親に持つ
- その親クラスがUITableViewDataSourceプロトコルを批准しており、かつプロトコルの必須メソッドを実装しているので問題ない
- 最終的にViewControllerクラスでは、その必須メソッドをoverrideして上書きして実装している
- その親クラスがUITableViewDataSourceプロトコルを批准しており、かつプロトコルの必須メソッドを実装しているので問題ない
- ViewControllerクラスではUITableViewControllerクラスを親に持つ
オプショナルバインディング・アンラップ
- オプショナルバインディング(アンラップ)
-
if let (定数) = (オプショナル型の値) { ... }
- オプショナル型の値がnil以外であれば
{}
内の処理が実行される
- オプショナル型の値がnil以外であれば
-
Segue
- Segue
- Storyboard上にある矢印のことで、画面遷移を表している
- Selection SegueとAccessory Actionがあり、それぞれ「セルがタップされた時」「アクセサリービューがタップされた時」に動作する
- アクセサリービュー:セルの右側に表示されるビュー(矢印アイコンやインフォーメーションアイコンなど)
- 基本的にはStoryboardにSegueを設定するだけで画面遷移を実装できるが、「画面間で値のやり取りをしたい場合」は独自の実装が必要
-
prepare()
メソッドにコードを書く
-
アイコン・起動画面
-
アプリアイコン
- 登録できる画像はPNGのみ
- 色々なサイズのアイコン画像が必要だが、一枚の画像からそれらを生成してくれるWebサービスなどもあるので活用する
- アイコンの角丸処理はiPhone側が自動的に行なってくれるので、普通の正方形のファイルを登録すれば良い
-
起動画面
- アプリアイコンをクリックしてからアプリ画面が開くまでの間を繋ぐ画面
- 初期画面に近い画像または、アプリのロゴや会社のロゴなどが入った画像を指定するのが一般的
- Appleは前者を推奨している(ただし誤操作を防ぐためにボタン類は全て非表示にしておくのがベター)
- 初期画面に近い画像または、アプリのロゴや会社のロゴなどが入った画像を指定するのが一般的
- アプリアイコンをクリックしてからアプリ画面が開くまでの間を繋ぐ画面
情報の調べ方
- 何らかの機能を実現したいと考えた際の調べ方
- 前提として、Appleがフレームワークを提供していない場合、実現は難しい(特にハードウェア操作系)
- まずは Apple Developer でドキュメントやチュートリアルを調べるのが手っ取り早い
- Apple Developer Forums などを見るのも良い(デベロッパプログラム登録者のみ)
- 一応日本語ドキュメントもある 日本語ドキュメント - Apple Developer)
- Xcode内蔵の「Developer Documentationを見るのも良い
- 日本語ドキュメントをググるのも有り
- 最後に公式APIリファレンスで詳細情報を確認する
その他
-
アセットカタログ
- イメージセットをまとめているフォルダ
- イメージセット:各デバイスのディスプレイごとの画像を登録しておく機能
- イメージセットをまとめているフォルダ
-
Any型
- 「どんな型でも」という意味で、「iOSが扱うことのできる全てのデータ(インスタンス)に対応する型」のこと