Help us understand the problem. What is going on with this article?

Parseを利用したiOSアプリケーションの試作(第二回)

More than 3 years have passed since last update.

今日は画面を追加して、テキストフィールドやテキストビューに入力したデータをParseに投稿できるようにします。
前回の記事をお読みでない方は、下記記事をご参照ください。

完成イメージ

parsetest02

画面の作成

まず最初にStoryBoardを使用して、画面から作成していきます。

デフォルトで追加されているViewControllerは一旦削除して、新たにTableViewControllerを追加します。

[Editor] - [Embed in] - [Navigation Controller]からNavigation Controllerを設定後、Navigation Controllerを"Is Initial View Controller"に設定します。

また、データを登録(参照)する画面としてViewControllerを追加し、TableViewCellからSegueで遷移できるように"control"キーを押しながらドラッグし接続します。

Segueは、Showで接続します。

一通り画面を作ったら、コードを書いていきます。
[File] - [New] - [File]から"Swift File"を選択し、下記クラスの設定をしてください。

TableViewController

import UIKit
import Parse
import ParseUI

class TableViewController: PFQueryTableViewController {
    
    override init(style: UITableViewStyle, className: String!) {
        super.init(style: style, className: className)
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        // Configure the PFQueryTableView
        self.parseClassName = "Articles"
        self.pullToRefreshEnabled = true
        self.paginationEnabled = true
        self.objectsPerPage = 30
    }
    
    // Define the query that will provide the data for the table view
    override func queryForTable() -> PFQuery {
        var query = PFQuery(className: "Articles")
        query.orderByAscending("createdAt")
        return query
    }
    
    //override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {

        var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! PFTableViewCell!
        if cell == nil {
            cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
        }

        // Extract values from the PFObject to display in the table cell
        if let title = object?["title"] as? String {
            cell.textLabel?.text = title
        }
        if let body = object?["body"] as? String {
            cell.detailTextLabel?.text = body
        }
        if let user_id = object?["user_id"] as? String {
            cell.detailTextLabel?.text = user_id
        }
        
        return cell
    }
    
    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // Get the new view controller using [segue destinationViewController].
        var detailScene = segue.destinationViewController as! DetailViewController

        // Pass the selected object to the destination view controller.
        if let indexPath = self.tableView.indexPathForSelectedRow() {
            let row = Int(indexPath.row)
            detailScene.currentObject = (objects?[row] as? PFObject)
        }
    }

    override func viewDidAppear(animated: Bool) {

        // Refresh the table to ensure any data changes are displayed
        tableView.reloadData()
    }
}

設定する部品(コントローラ)を選択してから、Identity Inspectorに表示されいるClassを設定します。

DetailViewController

import UIKit
import Parse
import ParseUI
import Bolts

class DetailViewController: UIViewController{
    
    @IBOutlet weak var article_title: UITextField!
    @IBOutlet weak var body: UITextView!

    // Container to store the view table selected object
    var currentObject : PFObject?
    

    @IBAction func saveButton(sender: UIBarButtonItem) {
        if let updateObject = currentObject as PFObject? {
            
            // Update the existing parse object
            updateObject["title"] = article_title.text
            updateObject["body"] = body.text
            
            // Save the data back to the server in a background task
            updateObject.saveEventually()
        }else {
            
            // Create a new parse object
            var updateObject = PFObject(className:"Articles")
            
            updateObject["title"] = article_title.text
            updateObject["body"] = body.text
            
            // Save the data back to the server in a background task
            updateObject.saveEventually()
        }
        
        // Return to table view
        self.navigationController?.popViewControllerAnimated(true)
    }
    
    
    override func viewDidLoad() {
        // Unwrap the current object object
        if let object = currentObject {
            article_title.text = object["title"] as! String
            body.text = object["body"] as! String
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

TableViewControllerと同様にクラスを作成し、StoryBoardでクラスの設定を行います。

上記以外に、TableViewで使用するCellの設定が必要で、ClassとStyle、Identifierを以下のように設定します。

テストデータの作成

クラス名を変更したらWebブラウザからParseの管理画面を開き、テストデータを登録します。
最初に、SQLでいうところのテーブルにあたるクラスを作成してください。

今回は、簡易的なブログを想定しているため、"Articles"と命名しました。

次に、カラムを追加します。タイトルを表す"title"と本文を表す"body"を追加します。

定義ができたら、実際にそれらしいデータをエクセルのように入力しておいてください。

ここまでできたら、プログラムを実行します。

上記のようにParseのDBのデータが表示されましたでしょうか?

登録(編集)画面の作成

データを登録したり、編集したりできるように、DetailViewControllerにTextFieldとTextViewを追加し、プログラムから操作できるようControlで接続します。

各部品は、画面右下のObject Libraryから追加できます。

ただ追加しただけでは、各デバイスでのレイアウトが崩れてしまうため、制約(Constraints)を追加してやる必要があります。が、ここではその説明は長くなるため割愛させていただきます。

各オブジェクトが配置できたら、コードからプロパティとして扱えるようにConnectします。Connectには大きく2つがあります。

  • Outlet:プロパティとして扱う
  • Action:タップされた場合など、予め紐付けたメソッドを呼び出す

上図のように、アシスタントエディアモードにして、"control"キーを押しながらプロパティを定義するあたりにドラッグしてやります。NavigationBarには、データを保存するようの"Save"ボタンを追加し、空の状態でDetailViewを表示するaddメソッドを実装します。(既に紹介したコードには、addメソッド実装済み)
また、バックグラウンドでデータ保存を行えるようにするために、フレームワークBoltsもインポートしておきます。

完成

だいぶ長くなってしまいましたが、これで冒頭で紹介した完成イメージのように動作するはずです。
いかがでしたでしょうか。Parseの使い方、iOSアプリケーションの開発の流れを掴んでいただけたら幸いです。

書ききれていない部分もあるので、詳細はGitのコードを見てみてください。

次回は、ログイン機能を構築してみます。

初歩的なエラーにハマった話(余談)

DetailViewControllerの"Title"プロパティ追加時に、下記エラーが発生

  • Cannot override strong property with weak property
  • Property 'title' with type 'UITextField!' cannot override a property with type 'String?'
  • Getter for 'title' with Objective-C selector 'title' conflicts with getter for 'title' from superclass 'UIViewController' with the same Objective-C selector
  • Setter for 'title' with Objective-C selector 'setTitle:' conflicts with setter for 'title' from superclass 'UIViewController' with the same Objective-C selector

原因:UIViewControllerの"title"プロパティと被っていたためでした。
参考:iOS Developer Library - UIKit Framework Reference - UIViewController Class Reference

GitHub

https://github.com/kazuyatamakoshi/ParseSample01

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away