- 更新日: 2017年4月19日
- Swift & iOS
Swiftで作るToDoアプリ開発チュートリアル(iPhoneアプリ開発入門)
Swift と Xcode を使っての iOS 開発で、ToDo アプリを作ってみる開発のチュートリアルです。ちょっと長いですが、スクリーンショット付きで Xcode の操作方法を解説して、必要となる Swift のソースコードも全て掲載しています。以下画像のような、オーソドックスな ToDo アプリを作っていきます。
Todo アプリを操作するデモ動画を YouTube に上げました。以下から確認できます。
Swift/iOS Todo App demo | YouTube
私は Swift と Xcode に取り組み始めて1ヶ月程度の iOS 開発の入門者です(記事公開時の2015年6月当時)。iOS 開発を始めて感じたのは、Swift の文法よりも Xcode の操作が難しいことでした。特に Storyboard で UI を組み立てる作業でつまづきが多かったですので、このチュートリアルでは Xcode / Storyboard のスクリーンショットを豊富に掲載して、GUI 画面での操作が分かりやすくなるように心がけました。間違った記載などがありましたら、ご指摘頂けると助かります!
これから Swift / iOS 開発に取り組み始められる方に参考になれば幸いです。基本的な操作・作業を忘れないように、自分用の備忘録でもあります。Swift の文法や Xcode の基本的な操作法、Storyboard, Auto Layout の基本的な使い方などについては、詳しくは触れていません。初心者の方は、前もってドットインストールなどで学習されておくと分かりやすいかと思います。
iPhoneアプリ開発入門 (全13回) – プログラミングならドットインストール
— このチュートリアルの対象読者 —
Swift / iOS 開発の入門者〜中級者
— 詳しく説明しないこと —
Swift の文法
Xcode の基本的な操作方法
Storyboard の基本的な使い方
— 動作検証の環境 —
Mac OS X Yosemite 10.10.3
Swift 1.2
Xcode 6.3.1 および 6.3.2
Xcode iOS Simulator 8.3
MagicalRecord 2.2
【追記 2017/02/22】
Swift や Xcode、MagicalRecord 等のバージョンアップに伴い、この記事の動作検証の環境とバージョンが異なる場合は、エラーが起こる場合があります。公式ドキュメントや他ウェブページを参考にして、お使いのバージョンでのご対応をお願いいたします。
以下のQiita記事等をご参考お願いいたします。
[Swift]Swiftの勉強のためにToDoアプリを作ってみた – Qiita
【追記ここまで】
UI は Storyboard を使って組み立てていきます。また、CoreData(iOS 用 SQLite データベースを使うフレームワーク)のラッパーライブラリである MagicalRecord を使用します。
— 目次 —
・Xcode 新規プロジェクトの作成
・MagicalRecord のインストール
・Swift から MagicalRecord を使うための設定
・Todo リスト一覧の画面を作成する
・Table View の delegate と dataSource の設定
・Navigation Controller の追加と Navigation Item の設定
・Todo タスク追加の画面を作成する
・Cancel, Save ボタンの Action と Text Field の Outlet を作成
・Todo タスク用のモデルを作成
・UITableViewDelegate, UITableViewDataSource のメソッドを実装
・動作確認のためにビルドしてみる
・Todo タスクを追加できるようにメソッドを実装
・スワイプで Todo タスクを削除できるようにする
・Todo List 一覧 → Todo Item の詳細&編集への画面遷移
・Todo Item の Text Field に選択されたタスクを表示
・Todo タスクを編集できるようにする
・遭遇したエラー達
それでは、チュートリアル開始です!
Xcode 新規プロジェクトの作成
まず最初に Xcode 用新規プロジェクトの作成です。
Xcode を起動して Create a new Xcode project をクリックします。
あるいは、File → New → Project から新規プロジェクトを作成します。
続いて、iOS Application → Single View Application を選択して Next。
プロジェクト名などを入力します。
Product Name: TodoApp
Organization Name: 自分の名前や所属する組織名など
Organization Identifier: 所有するドメイン名を逆さにしたもの
Language: Swift
Devices: iPhone
use CoreData: チェック
Organization Name, Organization Identifier は任意に入力します。また Swift で開発するので Language は Swift を選択し、CoreData を使用するので use CoreData にもチェックを入れる。
プロジェクトの場所を選択して作成。
プロジェクトを保存する場所を選択します。Source Controle にチェックを入れて、「Create Git repository on My Mac」として Create をクリック。
以上で Xcode の新規プロジェクト作成は完了です。ここで一旦、Xcode を終了させておきます。
MagicalRecord のインストール
次に CocoaPods を使って MagicalRecord をインストールします。CocoaPods の使い方については以下を参照。
CocoaPodsの使い方、iOSライブラリの管理ツール | EasyRamble
MagicalRecord は、CoreData を Rails の ActiveRecord のように扱いやすくするライブラリです。CoreData は iOS 開発で sqlite などデータベースを使う際の OR マッパー的なフレームワークなのですが、そのままでは操作がやや煩雑ですので MagicalRecord を通して利用します。
MagicalRecordをSwiftで使う導入〜設定までの手順 | EasyRamble
コマンドラインから以下の手順で、CocoaPods で MagicalRecord をインストールします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ cd /path/to/XcodeProject/TodoApp $ pod init $ vi Podfile platform :ios, '8.3' target 'SwiftToDo' do pod 'MagicalRecord' end target 'SwiftToDoTests' do end $ pod install |
【追記 2017/02/22】
MagicalRecord のバージョンを固定する場合は、以下のように記述します。
1 2 3 |
target 'SwiftToDo' do pod 'MagicalRecord', '2.2' end |
【追記ここまで】
TodoApp.xcworkspace が生成されますので、以降は Xcode でこれを利用するようにする。TodoApp.xcworkspace を Xcode で開きます。コンソールから開くには以下。
1 2 3 |
$ open TodoApp.xcworkspace |
TodoApp.xcworkspace を開いて Xcode を起動しますと、ファイル・グループの横に ?(はてな)マークが付いています。
これは Git で add されていないものなので、Git の管理下にすれば ? マークが消える。Pods フォルダは MagicalRecord などライブラリが置かれるフォルダなので、Git 管理から除外します。
1 2 3 4 5 |
$ echo "Pods" >> .gitignore $ git add . $ git commit -m "pod install MagicalRecord" |
以上の作業で、はてなマークが消えるはずです。以降説明には含めていませんが、作業の折り目で Git でコミットすると良いかと思います。Xcode の Source Control を使うと、GUI で Git の操作を行えます。私はコマンドラインのほうが慣れているので、コマンドで Git 操作しています。
【追記 2015/07/02】
Xcode を使った iOS 開発での .gitignore の詳細については、以下 Qiita の記事をご参照お願いします。
XCodeを使用する場合の.gitignoreの内容及び、その解説 – Qiita
上で説明したコマンドでは、私は Rails の gem 管理(/vendor/bundle 以下)と同じ感覚で、Pods ディレクトリを .gitignore に追加したのですが、Pods ディレクトリを git 管理するかどうかは、プロジェクトのワークフローによるそうです。
あと後日に、*.xccheckout というファイルが原因で、ブランチを merge できないという問題に遭遇しました。上記のエントリーを参考にして、*.xccheckout を .gitignore に含めたら問題なく merge できるようになりました。
なので、本格的な開発時には .gitignore を適切な内容にしておくことが必要かと思われます。
【追記ここまで】
Swift から MagicalRecord を使うための設定
次に Swift から MagicalRecord を利用するための設定です。MagicalRecord は Objective-C 製のライブラリなので、Swift から利用するためには、Bridging Header という設定を行う必要があります。まずは、Bridging Header 用のファイルを作成します。
TodoApp グループを右クリック → New File を選択。
iOS → Source → Header File を選択して Next。
ファイル名を TodoApp-Bridging-Header.h にして Create。
作成した TodoApp-Bridging-Header.h を以下の通りに編集します。
TodoApp-Bridging-Header.h
1 2 3 4 5 6 |
#ifndef TodoApp_TodoApp_Bridging_Header_h #define TodoApp_TodoApp_Bridging_Header_h #import "CoreData+MagicalRecord.h" #endif |
続いて、作成した TodoApp-Bridging-Header.h を Objective-C Bridging Header として利用できるように、ファイルへのパスを Xcode の指定の箇所で設定します。
TodoApp プロジェクトを選択 → Targets で TodoApp を選択 → Build Settings → All → Swift Compiler – Code Generation → Objective-C Bridging Header に先ほど作成した TodoApp-Bridging-Header.h のフルパスを入力します。
Objective-C Bridging Header の右横 TodoApp の下をダブルクリックすると、入力用フィールドが表示されます。
あるいは、Bridging Header の設定は以下ページの手順で行うと、若干簡略化されます。
Bridging Headerのファイル作成と設定を簡単に行う手順 | EasyRamble
続いて、MagicalRecord を利用するために、AppDelegate.swift を以下のように編集します。編集するメソッドは2つ。
AppDelegate.swift
1 2 3 4 5 6 7 8 9 10 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // for magical record - initialize coredata MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed("TodoApp.sqlite") return true } func applicationWillTerminate(application: UIApplication) { // for magical record - clean up coredata MagicalRecord.cleanUp() } |
以上で、Swift から MagicalRecord を使うための設定は終了です。
以降、Storyboard を使って UI を作成していきます。
Todo リスト一覧の画面を作成する
Xcode で既に作成されている ViewController.swift を選択して、TodoListViewController.swift にリネームします。
↓
TodoListViewController.swift は、Todo リスト一覧画面のコントローラー用のファイルとなります。
ファイル名の変更に合わせて、クラス名も ViewController → TodoListViewController に変更。
TodoListViewController.swift(元の ViewController.swift)
1 2 3 |
class ViewController: UIViewController { // ... } |
↓
1 2 3 |
class TodoListViewController: UIViewController { // ... } |
続いて、Main.storyboard を選択して Storyboard を表示させます。
View Controller を選択して、Identity Inspector の Class を TodoListViewController に変更します。
次に、Todo List View Controller に Table View を配置していきます。
右下のオブジェクト・ライブラリの検索ボックスに「table」と入力して、Table View オブジェクトを選択します。
選択した Table View オブジェクトを、中央の Main.storyboard のエディタエリアの Todo List View Controller にドラッグ・アンド・ドロップする。
上下左右方向の中心を表すグリッド線が表示されるので、それを頼りに真ん中に Table View を配置します。上記のような位置になればOK。
次に Table View Cell オブジェクトを選択し、Table View の中にドラッグ・アンド・ドロップする。
上部に Prototype Cells という表示が現れます。
続いて、追加した Table View Cell オブジェクトに対応するクラスファイルを作成します。
Todo フォルダを選択して右クリック → New File。
iOS → Source → Cocoa Touch Class を選択して Next。
Subclass of をドロップダウンメニューから選び、Class の名前を入力します。
Class: TodoListItemTableViewCell
Subclass of: UITableViewCell
Language: Swift
として Next。
TodoApp の Group に作成。
上記画像のようになっていることを確認して Create。Table View のセルに表示する内容を色々と編集する場合は、ここで作成した TodoListItemTableViewCell.swift のファイルを編集します。今回はそのままです。
作成した TodoListItemTableViewCell クラスを、Storyboard 上の Table View Cell に割り当てます。Main.storyboard を開く。
Table View Cell を選択後、Identity inspector の Class に TodoListItemTableViewCell を選択。
同様に Attribute inspector の Identifier に TodoListItem と入力。
以上で、Todo List View Controller の中に Table View および Table View Cell を配置できました。
Table View の delegate と dataSource の設定
続いて、Table View を利用するために delegate と dataSource の設定を行います。View Controller から Table View を利用するためには、UITableViewDelegate プロトコルと UITableViewDataSource プロトコルを View Controller で継承して、プロトコルであらかじめ決められたメソッドを実装する必要があります。
まずは、Table View から delegate と dataSource を設定するために、Main.storyboard を開きます。
Table View を選択し Control キーを押しながら、Todo List View Controller にドラッグ・アンド・ドロップする。
以下画像のように表示されるので delegate を選択する。
同じ作業をもう一度繰り返し、dataSource を選択します。
続いて、TodoListViewController クラス に Table View の Outlet を作成します。Outlet は Storyboard 上のオブジェクトを、ソースコード上の変数として使えるようにするための仕組み(と理解しています)。Todo List View Controller を選択して、アシスタントエディタに TodoListViewController.swift を表示させる。
Table View を Control キーを押しながら、TodoListViewController クラスのソースコードにドラッグ・アンド・ドロップ。
Outlet の Name に tableView と入力。
Connection: Outlet
Name: tableView
Type: UITableView
Storage: Weak
として Connect ボタンをクリック。
TodoListViewController.swift のソースコード上では以下のようになりました。
TodoListViewController.swift
1 2 3 4 5 6 |
class TodoListViewController: UIViewController { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { // ... } |
ここまでの作業で、Table View の Connections inspector が次のとおりになっていることを確認する。
TodoListViewController.swift を開いて、UITableViewDelegate, UITableViewDataSource を使用(継承)することをソースコードにも追加します。
TodoListViewController.swift
1 2 3 4 5 6 |
class TodoListViewController: UIViewController { @IBOutlet weak var tableView: UITableView! // ... } |
↓
1 2 3 4 5 6 |
class TodoListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! // ... } |
TodoListViewController.swift に UITableViewDelegate, UITableViewDataSource の継承を追記すると、エラーが表示されるかと思います。
1 2 3 |
Type 'TodoListViewController' does not conform to protocol 'UITableViewDataSource' |
これは UITableViewDataSource の protocol 継承で、実装すべきメソッドを実装していないのが原因です。とりあえず、先に Storyboard での UI 作りを先に終えたいので、これらのメソッドの実装は後に回します。
Navigation Controller の追加と Navigation Item の設定
画面遷移のための Navigation Controller を追加します。Main.storyboard を開く。
Todo List View Controller を選択した状態で、上部メニューの「Editor → Embed In → Navigation Controller」をクリックして、Navigation Controller を選択。
Storyboard エディタエリアが以下の状態になります。
Todo List View Controller → Navigation Item を選択し、Attributes Inspector の Title に「Todo List」と入力。
Navigation Item の表示が Todo List に変わり、また Todo List View Controller の上部に Todo List とタイトルが表示される。
次に、新しいタスクを追加する時に押すための + ボタンを作ります。右下オブジェクト・ライブラリの検索ボックスに「bar button」と入力して Bar Button Item を選択。
Bar Button Item を Todo List(Navigation Item)の右端(バッテリー表示のすぐ下)にドラッグ・アンド・ドロップ。
Bar Button Item を選択した状態で、Attributes Inspector の Identifier を Add に変更。
表示が Item から + に変わったのを確認します。
Todo タスク追加の画面を作成する
次に、Todo リスト一覧の画面の +(Add)ボタン(Bar Button Item)を押した後に遷移する、Todo タスク追加の画面を作成していきます。この画面は後で作成する、Todo 詳細&編集画面と共通のビューとなります。
Main.storyboard でオブジェクト・ライブラリから View Controller を見つけて、Storyboard にドラッグ・アンド・ドロップ。
Todo List View Controller の「+ボタン」を選択して、Control キーを押しながら、追加した View Controller の上にドラッグ・アンド・ドロップします。
Action Segue の show を選択。
Todo List View Controller と View Controller が + ボタンを介してつながりました。
オブジェクト・ライブラリから Navigation Item を View Controller にドラッグ・アンド・ドロップ。
Navigation Item(Title)を選択し、Attributes inspector で Title を「Todo Item」に変更。
Bar Button Item を Todo Item の左端にドラッグ・アンド・ドロップして、Identifier を Cancel に変更。
同様に Bar Button Item を Todo Item の右端にドラッグ・アンド・ドロップして、Identifier を Save に変更。
この Save ボタンが Todo タスクを追加して保存するためのボタンとなります。続いて、Todo タスクを文字で入力するためのフィールドを作る。
オブジェクト・ライブラリから Text Field を View Controller(Todo Item)にドラッグ・アンド・ドロップ。
Text Field のサイズと位置は適当に調整しておきます。
ここで、Auto Layout の設定を行いますが、今回は Xcode が提案する Auto Layout の自動設定を行います。1つずつの部品に対して、細かく Auto Layout を手動で設定することもできますが、このチュートリアルでは Auto Layout の詳細には触れません。Auto Layout の詳細については、冒頭で紹介したドットインストールを参照。
Todo List View Controller(Todo 一覧リスト)を選択した状態で、Editor → Resolve Auto Layout → Reset to Suggested Constraints をクリックします。
Todo タスク追加/詳細ページ用の View Controller(Title)についても同じ作業を行う。
続いて、Todo タスク追加/詳細ページの View Controller に対応する TodoItemViewController.swift ファイルを作成します。
TodoApp グループから New File。
iOS → Source → Cocoa Touch Class を選択して Next。
UIViewController のサブクラスとして、TodoItemViewController クラスを作成します。
TodoApp グループに Create。
Main.storyboard を選択して、Todo タスク追加/詳細ページ View Controller の Identity inspector で、Class に TodoItemViewController を指定する。
Cancel, Save ボタンの Action と Text Field の Outlet を作成
Main.storyboard で Todo Item の View Controller を選択し Asistant Editor を開く。Cancel, Save ボタンが押された時に対応する action を設定します。
Cancel ボタンを、Control キーを押しながら TodoItemViewController のソースコードにドラッグ・アンド・ドロップ。
cancel という名前の Action を作成。
Connection: Action
Name: cancel
Type: UIBarButtonItem
として、Connect ボタンをクリック。
同様に Save ボタンを、Control キーを押しながら TodoItemViewController にドラッグ・アンド・ドロップ。
save という名前の Action を作成。
Connection: Action
Name: save
Type: UIBarButtonItem
として、Connect ボタンをクリック。
TodoItemViewController.swift に追加された @IBAction で始まる行を確認して、コードを以下のように編集します
TodoItemViewController.swift
1 2 3 4 5 6 7 |
@IBAction func cancel(sender: UIBarButtonItem) { navigationController!.popViewControllerAnimated(true) } @IBAction func save(sender: UIBarButtonItem) { navigationController!.popViewControllerAnimated(true) } |
各々 Cancel, Save ボタンが押された時のアクションを実装するメソッドとなります。ここでは、簡易的に popViewControllerAnimated で1つ前の画面に戻るように実装しています。実際にキャンセルや保存を行う実装は後で行います。
Todo Item の View Controller の Connections inspector で Received Actions(画像一番下)が設定されていることを確認。
続いて、文字の入力フィールドである Text Field の Outlet の設定を行います。Main.storyboard で Asistant Editor を開く。
Text Field を Control キーを押しながら、TodoItemViewController クラスの画像の位置にドラッグ・アンド・ドロップ。
todoField という名前の Outlet を作成。
Connection: Outlet
Name: todoField
Type: UITextField
Storage: Weak
として Connect。
TodoItemViewController.swift
1 |
@IBOutlet weak var todoField: UITextField! |
が追加されたのを確認します。これで、Text Field の Outlet を作成できました。
Todo タスク用のモデルを作成
Todo タスク用モデルで利用する CoreData のエンティティを作成します。CoreData のエンティティとは、RDBMS でいうテーブルのような概念っぽい。
TodoApp.xcdatamodeld を選択し、下記の画像のようにエンティティを作成。
Entitiy: Todo
Attribute: item
Type: String
下部の Add Entitiy をクリックして追加。名前を Todo にする。また Attributes の下の + をクリックして属性を追加、名前は item で、型は String を指定。
続いて、モデル用のファイルを作成します。
TodoApp.xcdatamodeld を選択した状態で、Editor → Create NSManagedObject Subclass をクリック。
TodoApp にチェックして Next。
Todo にチェックして Next。
TodoApp グループに Create。
Todo.swift が作成されたことを確認し、@objc(Todo) の行を追加します。
Todo.swift
1 2 3 4 5 6 7 8 9 |
import Foundation import CoreData @objc(Todo) class Todo: NSManagedObject { @NSManaged var item: String } |
TodoApp.xcdatamodeld を選択し、Data Model inspector で Name を Todo、Class を Todo に変更します。
一連の作業で作成した CoreData のモデル(Todo モデル)に対して、MagicalRecord を使って CRUD 操作を行うことになります。
UITableViewDelegate, UITableViewDataSource のメソッドを実装
ここで、エラーを放置していた TodoListViewController.swift で、UITableViewDelegate, UITableViewDataSource のメソッド、および保存済みのタスク一覧を取得する処理を実装する。
TodoListViewController.swift を以下のように編集します。
TodoListViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import UIKit import CoreData class TodoListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var todoEntities: [Todo]! override func viewDidLoad() { super.viewDidLoad() todoEntities = Todo.MR_findAll() as? [Todo] } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return todoEntities.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("TodoListItem") as! UITableViewCell cell.textLabel!.text = todoEntities[indexPath.row].item return cell } } |
import CoreData で CoreData を使うことを宣言。todoEntities というプロパティに、Todo モデルのインスタンスを配列として保持するようにしています。
viewDidLoad() メソッドでは、MagicalRecord の MR_findAll() メソッドを使って、Todo をすべて読み込んでいます。これで Todo リストの一覧を取得する。
追加した2つの tableView メソッドは、UITableViewDataSource 継承により実装しなければならないメソッドです。各々、セクションに含まれる行数を返すメソッド、およびセルに表示する内容を設定してセルを返すメソッドとなります。これらのメソッドを実装することで、表示されていたエラーが消えるはずです。
動作確認のためにビルドしてみる
ここで一旦ビルドを実行してみましょう。Xcode 左上部の三角マークをクリックします。
「Do you want the application “Xcode.app” to accept incoming network connections?」とダイアログボックスが表示されたら、Allow をクリックします。
シミュレーターが起動して動作を確認できる。Todo List 画面。
+ ボタンをタップすると、Todo Item 画面に遷移。
Todo List のページで + ボタンをクリックすると、Todo Item のページに移動し、Cancel または Save を押すと元の Todo List のページに戻ることが確認できます。
まだ必要な実装を行っていないため、実際のタスク追加・編集・削除などは動作しない状態です。
Todo タスクを追加できるようにメソッドを実装
Todo タスクを保存できるようにするために、TodoItemViewController.swift の save メソッドを実装する。Todo 追加画面で Text Field にタスクを入力後、Save ボタンが押された時に実行されるメソッドです。
TodoItemViewController.swift の save メソッドを以下のように編集。
TodoItemViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import UIKit import CoreData class TodoItemViewController: UIViewController { // ... @IBAction func save(sender: UIBarButtonItem) { let newTask: Todo = Todo.MR_createEntity() as! Todo newTask.item = todoField.text newTask.managedObjectContext!.MR_saveToPersistentStoreAndWait() navigationController!.popViewControllerAnimated(true) } } |
MagicalRecord の MR_createEntity() メソッドで新たなタスクを作成し、Text Field の入力文字を設定した後、MR_saveToPersistentStoreAndWait() メソッドで、新たに作られたタスクを保存しています。
また、TodoListViewController.swift に viewWillAppear メソッドを追加します。
TodoListViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class TodoListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var todoEntities: [Todo]! override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) todoEntities = Todo.MR_findAll() as? [Todo] tableView.reloadData() } override func viewDidLoad() { super.viewDidLoad() todoEntities = Todo.MR_findAll() as? [Todo] } // ... } |
viewWillAppear() では、viewDidLoad() と同様に Todo タスク全一覧を取得しています。
ここで動作確認のためにビルド実行しましょう。Todo List 一覧画面で、右上の + ボタンからタスクが追加できるようになりました。
タスクを入力して Save ボタンを押すと、Todo List 画面に反映されます。いくつかタスクを追加した後の Todo List 画面。
スワイプで Todo タスクを削除できるようにする
次に Todo List の画面でスワイプで、保存済みの Todo タスクを削除できるようにします。
TodoListViewController.swift に以下のとおりメソッドを追加。
TodoListViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class TodoListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // ... func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { if editingStyle == .Delete { todoEntities.removeAtIndex(indexPath.row).MR_deleteEntity() NSManagedObjectContext.MR_defaultContext().MR_saveToPersistentStoreAndWait() tableView.reloadData() } } // ... } |
MagicalRecord の MR_deleteEntity() メソッドで、Todo タスクを削除するようにしています。
確認のためにビルド実行。
Todo List 画面で、アイテムをスワイプすると以下のように Delete ボタンが表示され削除できます。
Todo List 一覧 → Todo Item の詳細&編集への画面遷移
続いて、Todo List 一覧画面 → Todo Item の詳細&編集画面に移動できるようにします。Todo Item の詳細&編集画面は、タスク追加の画面と共通です。Main.storyboard を開きます。
TodoListItem(Table View Cell)を選択し Control キーを押しながら、Todo Item の View Controller にドラッグ・アンド・ドロップ。
Selection Segue で show を選択。
TodoListItem → Todo Item の矢印を選択し、その Attributes inspector で Identifier に edit と入力します。
edit は画面遷移を識別するための識別子となります。これで、Prototype Cells(Todo List 一覧画面のテーブルのセル)→ Todo Item(Todo 詳細&編集画面)がつながりました。
TodoListViewController.swift に、画面遷移のための以下のメソッドを追加。
TodoListViewController.swift
1 2 3 4 5 6 7 8 9 10 11 |
class TodoListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // ... override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "edit" { let todoController = segue.destinationViewController as! TodoItemViewController let task = todoEntities[tableView.indexPathForSelectedRow()!.row] todoController.task = task } } } |
edit の識別子を持つ segue(画面遷移)が選択された時のアクションとなります。Todo List 画面で選択された(タップされた)タスクを、TodoItemViewController の task プロパティに設定しています。
続いて TodoItemViewController.swift を編集して、task プロパティを追加。
TodoItemViewController.swift
1 2 3 4 5 6 7 |
class TodoItemViewController: UIViewController { @IBOutlet weak var todoField: UITextField! var task: Todo? = nil // ... } |
task プロパティは、Todo のオプショナル型の変数にします。
ビルド実行。
Todo List 画面でタスクををタップすると…
Todo Item に移動できるようになりました。
しかし、Todo Item に何も表示されていない状態ですので、Todo List でタップされたタスクを Text Field 内に表示させるようにしてみます。
Todo Item の Text Field に選択されたタスクを表示
TodoItemViewController.swift の viewDidLoad() メソッドを編集します。
TodoItemViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class TodoItemViewController: UIViewController { @IBOutlet weak var todoField: UITextField! var task: Todo? = nil override func viewDidLoad() { super.viewDidLoad() if let taskTodo = task { todoField.text = taskTodo.item } } // ... } |
ビルド実行。
これで Todo List 画面でタスクをタップすると…
Todo Item 画面の Text Field 内にタスク詳細が表示されるようになりました。
Todo タスクを編集できるようにする
続いて、Todo Item 画面で既存のタスクを編集できるようにします。TodoItemViewController.swift の save() アクションを以下のように変更します。
TodoItemViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class TodoItemViewController: UIViewController { // ... @IBAction func save(sender: UIBarButtonItem) { if task != nil { editTask() } else { createTask() } navigationController!.popViewControllerAnimated(true) } func createTask() { let newTask: Todo = Todo.MR_createEntity() as! Todo newTask.item = todoField.text newTask.managedObjectContext!.MR_saveToPersistentStoreAndWait() } func editTask() { task?.item = todoField.text task?.managedObjectContext!.MR_saveToPersistentStoreAndWait() } } |
task プロパティが空の時(既存タスクが存在しない)時は createTask() 呼び出しでタスクを新規作成、task プロパティが存在する(既存タスクが存在する)時は editTask() 呼び出しで編集するように変更しました。
また TodoListViewController.swift に controllerDidChangeContent() メソッドを追加。
TodoListViewController.swift
1 2 3 4 5 6 7 |
class TodoListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // ... func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.reloadData() } } |
ビルド実行。
以下画像のように、Todo Item 画面で既存のタスクを編集できるようになりました。
Todo List 画面で「buy a swift book」のタスクをタップして…
Todo Item 画面で「read a swift book」に変更して save。
Todo List 画面に変更が反映されました。
以上で Todo アプリの機能は作り終えました。長かった Swift を用いた Todo アプリ開発チュートリアルは終了です。おつかれさまでした!
冒頭でもお伝えしたとおり、私は Swift/iOS 開発歴がまだ1ヶ月程度の入門者です。もし間違っている箇所がありましたら、ご指摘頂けるとありがたく思います。よろしくお願いいたします。
この開発チュートリアルを作るにあたり、以下のウェブページを参考にさせて頂きました。ありがとうございました。
iOS Swift Tutorial: Core Data with MagicalRecord in Swift – James Leist
CoreData tutoiral in Swift using NSFetchedResultsController Ramblings on Swift
Bundlr – SwiftとMagicalRecordを使って簡単なTodoアプリを作るチュートリアル – Qiita
CocoaDocs.org – MagicalRecord Reference
遭遇したエラー達
最後にこの Todo アプリを作っていた時に遭遇したエラーを挙げて、その解決法を記しておきます。
・libc++abi.dylib: terminating with uncaught exception of type NSException
原因: モデルのファイルで @objc の宣言忘れ
Todo.swift
1 2 3 4 5 6 7 8 9 |
import Foundation import CoreData @objc(Todo) class Todo: NSManagedObject { @NSManaged var item: String } |
のように @objc(Todo) の行を追加することで解決できました。
ios – MagicalRecord createEntity error in Swift – Stack Overflow
NSManagedObjectサブクラスの@objc宣言忘れでNSExceptionエラー | EasyRamble
・this class is not key value coding-compliant for the key
・Terminating app due to uncaught exception ‘NSUnknownKeyException’
原因: コネクション・インスペクタでの outlet, action などの接続ミス
解決策は、コネクション・インスペクターでの接続を削除するとともに、ソースコード側の @IBOutlet, @IBAction などの行も削除した後に、Outlet や Action を作成しなおす。
「this class is not key value coding-compliant for the key」の原因 ← webに生きる人+α
Outlet削除でのNSUnknownKeyExceptionエラー | EasyRamble
- Swift & iOS の関連記事
- WKWebView/UIWebViewでウェブページが真っ白
- Unityのインストールと初期設定
- WKWebView/UIWebViewでNavigation Barの下にウェブページが隠れるのを回避
- SwiftでArray(配列)などをシャッフル
- Navigation Controllerで画面遷移させるSwiftコード
- Swiftでタップ/スワイプのイベント処理実装・UITapGestureRecognizerとUISwipeGestureRecognizer
- UIPageViewController画面下部のUIPageControlを非表示にする
- Swiftのバージョン確認・REPL実行
- Xcode7.0アップデートで遭遇した課題2つ
- 正規のXcodeかどうかチェック(XcodeGhostマルウェア騒動)
- 初回公開日: 2015年6月8日
- 8件のコメント
非常に丁寧な内容で、参考になりました。ありがとうございました。
asan さん、ありがとうございます。そう言って頂けますと嬉しいです。
Swift バージョンが 2, 3 と上がってますので、いずれ内容をリライトしたいのですけどね。
とても丁寧な内容で、わかりやすかったです。
質問なんですが、Todo.Swiftを作成する際に、エラーが発生してしまいます。どうしたらいいか教えていただけませんか?
shun さん、記事をお読み頂き、またコメントありがとうございます。Swift や Xcode のバージョンがちょっと以前の内容ですので、そのあたりの違いが原因でエラーが発生するかもしれません。そこは適宜ご利用の環境に合わせて、修正や読み替えをお願いしたい所存です。
もう少し具体的にどのようなエラー(エラーログ等)が、どういった環境で発生するのかをお伝え頂ければありがたく思います。qiita 記事の質問フォーマットが分かりやすいかと思います。
http://qiita.com/seki_uk/items/4001423b3cd3db0dada7
以上ですが、よろしくお願いいたします。
わかりやすい記事をありがとうございます。
質問なのですが、手順通りにやったのですが#import “CoreData+MagicalRecord.h”とTodoApp-Bridging-Header.hでエラーが出てしまいfile not foundと忠告されました。
こちらはmagicalrecordが正常にインストールされていないという認識で大丈夫ですか?
お忙しいところ大変恐縮ですがお返事いただければ幸いです。
shota さんコメントありがとうございます。お返事が遅くなりすいません。
file not found ですので、ヘッダーファイル(TodoApp-Bridging-Header.h または CoreData+MagicalRecord.h)が見つかりません、という意味です。
可能性としては…
1. MagicalRecord のインストールが正常に行われていない
2. MagicalRecord のインストールは正常に行われているが、ヘッダファイルが上手く読み込めていない
のどちらかの原因があり得るかと思います。
ですので、以下をご確認されてみてください。
1. Pods フォルダに MagicalRecord がインストールされているかどうか
2. ヘッダーファイル(TodoApp-Bridging-Header.h および CoreData+MagicalRecord.h)の名前やパスが正しいか、また存在するかどうか
以上ですがよろしくお願いいたします!
横から申し訳ありません。
元コメの質問者様とは別人です。
私も昨日始めたのですが、
どうもCocoa PodsでインストールされるMagicalRecordのバージョンが記事中の2.2から2.3に上がったみたいですね。
それに伴い、例えば…
・元コメの質問にある#import “CoreData+MagicalRecord.h”がない(2.3では#import と書かないとだめみたいです。)
・AppDelegate.swiftに”import MagicalRecord”
を追加しないとビルドエラー(非推奨マークが一瞬出るので、これは私の設定がおかしいのかもしれませんが)&”MagicalRecord.setupCoreDataStack(withAutoMigratingSqliteStoreNamed: “TodoApp.sqlite”)”と微妙に自動修正される
みたいな違いが出ています。
正直、画面すら触ってないのにお腹がいっぱいとしんどくなってきました…
昨日始めた若輩者ゆえ恐縮なのですが
Podfileの設定に、MagicalRecordのインストールバージョンを2.2に固定する設定を入れてみてはいかがでしょうか。
大まかな流れは非常に分かりやすいので是非これからもこの記事をメンテしていただければ幸いです。
anko さん、コメントありがとうございます。動作検証の環境・バージョン等は明記していますので、それとバージョンが異なる場合はエラーが起こる可能性があります。
http://qiita.com/BlackCat/items/8dcbe1d6f996087ecc3b
を参考お願いいたします!