storyboardを使わずにToDoアプリを作ってみたら、とても勉強になりましたので、投稿します。
storyboardを使うか否かは趣味の問題もあるし、コードの量を減らせること考えたら、使った方が良いかと思うのですが。
僕はプログラム初心者なので、どうしてもstoryboard使うとプログラムの行間が分からなくなってしまうのです。なので一切storyboardを使わずにやりたくなってしまうのです。
以下のサンプルは(変数名が適当/サイズ指定が手抜き/テーブルからの削除機能とか未実装)だったりするけど、そんなことはどうでもよいのです!!(恥)
僕はこの課題を通して、Swiftのことを少し身近に感じることができたのですから。具体的にはいろんな機能とその使い方、ハマりどころなどを知る事ができたのですから。
皆さんの参考になれば、幸いでございます。
使っている主なおいしい機能
- NavigationController
- TableView
- CoreData
これらはアプリ開発におけるとても汎用性の高い機能たちだと思います。
完成図
参考情報
本投稿で全部書くと死んじゃうので、各機能の細かい部分について不明点などあれば以下の記事をご参照ください。
- [Swift] storyboardを使わず、テーブルを表示させる短いサンプル(UITableView)
- storyboardを使わず、Swiftでナビゲーションバーをつくる(UINavigationControllerとか)
- SwiftでCoreDataを使ってみる(Stepごとに解説)
- SwiftでCoreData「NSManagedObjectのプロパティが上手く取れない」ときの対処法
ソースコード
CoreDataを使うためのxcodemodeld
の設定以外は、4つファイル書いたらおわりです。
ちなみに、xcodemodeldは以下のようにしておき、class名も「ToDo1123.ToDoStore」とちゃんと書きます。
以下ソースコードです。
import UIKit
import CoreData
class ToDoStore: NSManagedObject {
@NSManaged var memo:String
@NSManaged var date:NSDate
}
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
/*
以下1行追加
*/
var myNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
/*
以下5行追加
*/
let first: ViewController = ViewController()
myNavigationController = UINavigationController(rootViewController: first)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = myNavigationController
self.window?.makeKeyAndVisible()
return true
}
// 以下デフォルトのままなので、省略。
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// ナビバーの右上ボタンを用意
var addBtn: UIBarButtonItem!
// テーブルを用意
var piyo: UITableView!
// テーブルに表示するアイテムの配列を用意
var memos: [NSString] = []
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Home"
// addBtnを設置
addBtn = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "onClick")
self.navigationItem.rightBarButtonItem = addBtn
// 画面サイズを取得
let width: CGFloat! = self.view.bounds.width
let height: CGFloat! = self.view.bounds.height
// テーブルを用意して、表示
piyo = UITableView(frame: CGRectMake(0, 0, width, height))
piyo.registerClass(UITableViewCell.self, forCellReuseIdentifier: "data")
piyo.dataSource = self
piyo.delegate = self
self.view.addSubview(piyo)
}
// viewDidLoadは最初の一回しか呼ばれないので、viewWillAppearを使うよ
override func viewWillAppear(animated: Bool) {
// CoreDataからデータを読み込んで配列memosに格納する
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let toDoContext: NSManagedObjectContext = appDel.managedObjectContext!
let toDoRequest: NSFetchRequest = NSFetchRequest(entityName: "ToDoStore")
// 並び順をdateの、昇順としてみる
toDoRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
toDoRequest.returnsObjectsAsFaults = false
var results = toDoContext.executeFetchRequest(toDoRequest, error: nil) as [ToDoStore]!
memos = []
for data in results {
memos.append(data.memo)
}
// テーブル情報を更新する
self.piyo.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// addBtnをタップしたときのアクション
func onClick() {
let second = EditViewController()
self.navigationController?.pushViewController(second, animated: true)
}
// セルの行数を指定
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return memos.count
}
// セルの値を設定
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("data", forIndexPath: indexPath) as UITableViewCell
cell.textLabel.text = memos[indexPath.row]
return cell
}
}
import UIKit
import CoreData
class EditViewController: UIViewController {
// TextFieldを用意
var memoField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.cyanColor()
self.title = "Edit"
// text field
memoField = UITextField(frame: CGRectMake(100, 100, 200, 30))
memoField.borderStyle = UITextBorderStyle.RoundedRect
self.view.addSubview(memoField)
// write button
let writeBtn: UIButton = UIButton(frame: CGRectMake(100, 150, 200, 30))
writeBtn.backgroundColor = UIColor.magentaColor()
writeBtn.setTitle("メモを保存", forState: UIControlState.Normal)
writeBtn.addTarget(self, action: "writeData", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(writeBtn)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// CoreDataへの書き込み処理(writeBtnのアクション)
func writeData() {
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let toDoContext: NSManagedObjectContext = appDel.managedObjectContext!
let toDoEntity: NSEntityDescription! = NSEntityDescription.entityForName("ToDoStore", inManagedObjectContext: toDoContext)
var newData = ToDoStore(entity: toDoEntity, insertIntoManagedObjectContext: toDoContext)
newData.memo = memoField.text
newData.date = NSDate()
}
}
以上です。全部コードで書いてもこれくらいなので、そんな長くは無いかと。。。
僕がハマったこと2つ!!
基本的にはStack Overflowとかで調べて進めていくと、なんとか動いたりするのですが、それでもよくわからなくて、心優しい達人に教わって解決したポイントのメモです。
executeFetchRequestの書き方
結論以下のように書きましょう。
var results = toDoContext.executeFetchRequest(toDoRequest, error: nil) as [ToDoStore]!
ちなみに、最初は以下のように書いておりました。
var results: NSArray! = toDoContext.executeFetchRequest(toDoRequest, error: nil)
そして、Entityのclass名はモジュールをprefixにして、ちゃんと書いておきましょう。
そうしないと、なんか上手くいきません。すくなくとも、僕は上手くいきませんでした。result.memo
がエラーになるのです。最初は苦肉の策でresult.valueForKey("memo") as String
とかで回避しておりました。
テーブル情報の更新
結論以下で解決です。
override func viewWillAppear(animated: Bool) {
// piyoはtableViewです。無意味な変数名つけてごめんなさい。
self.piyo.reloadData()
}
最初僕はreloadDataの存在を知りませんでした。そのおかげで、いろんな場所にテーブル表示させるコード書いてみたりして、本当に無意味な時間を長く過ごすことができました。(涙)
以上でございます。お役に立てば幸いです。
ありがとうございました。