今までアプリの開発をしたことはなかったのですが、
swiftに興味を持ったのとXcodeがあればすぐに開発できるみたいだったので
勉強ついでにtodoListのアプリを作ることにしました。
今回作るもの
【Xcode / Swift入門】簡単なToDoリストアプリを作ってみよう - Enjoy Our Life!!
上記サイトのtodoListを参考にして一通り実装したのですが、
terminating with uncaught exception of type NSException
というExceptionが発生してしまい画面が真っ白な状態になりました。
どうやらこのExceptionは初心者が絶対ぶつかるエラーのようで、
Storyboardとの接続ミスや、Identifierのタイプミスが原因なことがほとんどのようです。
調査してみたのですが原因を見つけることができず…別の方法を検討することにしました。
調べていくうちにswiftのバージョン4と3で、結構変更があることがわかりました。
私のバージョンは4で参考にしたサイトは3だったので、
この違いでエラーになってしまうのではないかと思い、
バージョン4の下記のtodoListを参考にすることにしました。
ToDo App (Swift 4 + Xcode 9.0) - YouTube
Xcodeの使い方
Xcode自体を使うことが初めてだったので、画面名称だったり使い方を調べておきました。
下記のサイトが丁寧に説明してくれていました。
Main.storyboardを作成
画面レイアウトの設定ができます。
- NavigationController
今回のtodoListを作る場合、オブジェクトの追加でTableViewとNavigationBarが必要になるのですが、
NavigationControllerを使うとこれだけでセグエの種類をShowで接続した画面のNavigationBarや、戻るボタンを自動で表示してくれます。
ページ上部にNavigationBarを表示して、複数画面を遷移させたい場合にはとても便利な部品です。
NavigationControllerを選択した状態で「is Initial View Controller」にチェックを入れると、
アプリが起動したときにこのViewControllerが表示されるようになります。
- Bar Button Item
NavigationBarの中に並べるボタンの部品です。
System Itemの設定を「Add」にすると追加用のアイコン「+」になります。
- Segueによる画面遷移
タスクを追加した遷移先をViewControllerで作成し、
先ほどつくった「+」ボタンを選択してcontrolボタンを押しながら遷移先へ持っていくと、
Segueでの画面遷移ができるようになります。
次に、遷移先画面でタスク追加用のText FieldやButtonを配置します。
- Text Field
プレースホルダーの設定ができます。他にも文字の中央寄せ等といった設定もできます。
タスク追加画面の実装
タスクを入力したあとに押すAddボタンの実装をするため、CocoaTouchClassを新規作成します。
クラス名を適当に決めたら(今回は「AddController.swift」)Main.storyboardでタスク追加画面のViewControllerを選択し、
Customクラスの設定を追加したクラスにします。
こうすることで、ViewとAddController.swiftが関連付けられたことになります。
Main.storyboardのタスク追加画面のViewを選択した状態で、
右上にある丸が重なったようなマークのアシスタントエディタを選択すると、
関連するファイルを隣に並べて表示してくれます。
textFieldを選択した状態でcontrolボタンを押しながらAddControllerクラスの適当な場所へ引っ張って配置します。
このときConnectionを「Outlet」に設定します。
同様にAddボタンを選択した状態でcontrolボタンを押しながらAddControllerクラスの適当な場所へ引っ張って配置します。
このときはConnectionを「Action」に設定します。
- Outlet接続
ストーリーボード上のオブジェクトの参照のこと。
テキストフィールドに文字をセットするような場合に使います。
- Action接続
ボタンを押したときに呼ばれます。
Addボタンの処理を実装(AddController)
textFieldの値を読み込んだあと、次のタスクを入力できるようにtextFieldを初期化します。
placeholderを設定することもできます。
@IBAction func addPressed(_ sender: UIButton) {
if (textField.text != nil) && textField.text != ""{
todoList?.append(textField.text!)
textField.text = ""
textField.placeholder = "Add more ?"
}
}
タスクの保存と読み込みを実装
新規でswiftファイルを作成し、下記関数を追加します。(今回作ったファイルは「Constants.swift」)
"todoList"というキーで配列todoListを保存します。
func saveData(todoList:[String]) {
UserDefaults.standard.set(todoList, forKey: "todoList")
}
保存内容があった場合にその内容を返却します。
func fetchData() -> [String]? {
if let todo = UserDefaults.standard.array(forKey: "todoList") as? [String] {
return todo
}
else {
return nil
}
}
AppDelegate.swiftを修正(ライフタイムイベント)
アプリをつくった段階でデフォルトでつくられるファイルのひとつ。アプリ全体のライフタイムイベントを管理するためのクラスです。
applicationWillResignActive -> アプリ閉じそうな時に呼ばれる
applicationDidEnterBackground -> アプリを閉じた時に呼ばれる
applicationWillEnterForeground -> アプリを開きそうな時に呼ばれる
applicationDidBecomeActive -> アプリを開いた時に呼ばれる
applicationWillTerminate -> フリックしてアプリを終了させた時に呼ばれる
今回は起動時に呼ばれそうなapplicationに保存内容を読み込む処理と、
アプリを閉じそうな時に呼ばれるapplicationDidEnterBackground、
終了させたときに呼ばれるapplicationWillTerminateに保存する処理を追加しました。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if let todo = fetchData() {
todoList = todo
}else {
todoList = [String]()
}
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
saveData(todoList: todoList!)
}
func applicationWillTerminate(_ application: UIApplication) {
saveData(todoList: todoList!)
}
ViewControllerの実装(テーブルビュー表示)
UITableViewにデータを表示するためには、UITableViewDelegateとUITableViewDataSourceをdelegateしなくてはならないようです。
delegateとは他のクラスに処理を委譲したり、通知したりする仕組みのことのようです。
delegateする対象のクラスを指定します。
override func viewDidLoad() {
super.viewDidLoad()
todoTableView.delegate = self
todoTableView.dataSource = self
}
さらに対象となるクラスにdelegate元のクラスを宣言します。
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
delegateメソッドを実装します。
- セルの行数を返す
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let todo = todoList {
return todo.count
} else {
return 0
}
}
- セルのテキストを追加する
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
if let todo = todoList{
cell.textLabel?.text = todo[indexPath.row]
}
return cell
}
- UITableViewのセルをスワイプで削除する
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
todoList?.remove(at: indexPath.row)
tableView.reloadData()
}
}
- 画面に表示された直後に読み込む処理を実装する
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
todoTableView.reloadData()
}
ここまできたらbuildしてアプリを起動させます。
タスクの追加と削除ができました。
ちなみにボタンやテキストフィールドの配置が中央寄せになっていない場合は、
項目を選択した状態で下のバーにあるAdd New Containsマークを選択して、
下記に設定することで変更することができました。
つくってみた感想
swiftのバージョンの変更が大きく古い資料が参考にならなかったりしたので、情報を探すのが大変でした。
NSExceptionは原因をコンソールに表示してくれないので自分で調べる必要があり、ハマってしまうとすごく時間がかかってしまいそうでした。
画面が読み込まれたときにどの処理が呼ばれて、遷移先でどの処理が動くといった流れがまだ把握しきれていないので、
その部分について理解を深めていく必要がありそうです。Xcodeで使える機能が他にもまだまだたくさんありそうなので、
作るものによって使い分けれるようにならなければいけないと思っています。
javaで使うOptional型がswiftだと ? とか ! で使えるので、視覚的にわかりやすかったです。
Xcodeを初めて使いましたが補完がたくさん使えて便利でした。
iPhoneアプリを初めて作りましたが、シミュレーターで実際に動かして確認できるので作れたときの達成感が嬉しかったです。
自分の痕跡を残すためにgithubにあげておきました。
GitHub - tominagamaya/todo-app-ios