本講義ではInstagramのような写真共有アプリを作ります。

完成品のサンプルは以下よりダウンロードできます。イメージが湧かない方は是非ご確認ください。
iOS完成品サンプル(写真共有アプリ)
講義の目標
- mBaasを使った画像共有の仕組みを理解すること
- 友達申請・承認機能の仕組みを理解すること
- mBaasを使った「いいね」機能の仕組みを理解すること
NCMB利用準備
今回もメッセンジャーアプリと同じくNCMB(Nifty Mobile Backend)を利用していきます。
photoShare
でアプリ登録しておいてください。

http://mb.cloud.nifty.com/
表示されたアプリケーションキー
、クライアントキー
は保存しておいてください。

Xcodeプロジェクト作成
PhotoShare
でXcodeプロジェクトを新規作成してください。
使用するテンプレートはSingle View Application
となります。

入力項目 |
入力値 |
Product Name |
PhotoShare |
Team |
None |
Oganization Name |
任意(”ALJ”など自分が所属している組織の名前を入れる) |
Oganization Identifier |
jp.co.al-j |
Language |
Swift |
Devices |
iPhone |
Use Core Data |
チェックを外す |
include Unit Tests |
チェックを外す |
include UI Tests |
チェックを外す |
ライブラリ追加(Cocoapod)
今回もNCMBのライブラリを利用しますのでCocoapod
を使い、ライブラリをインポートします。
プロジェクトフォルダをターミナルにドラッグします。

ターミナルの画面でpod init
を実行します。
これでPodfile
がプロジェクト内に作成されますので、open
コマンドで開きましょう。
テキストエディタが開いてPodfile
を編集できますので、下記内容になるように修正し、保存してください。
Podfile
platform :ios, '8.0' use_frameworks!
target 'PhotoShare' do pod 'NCMB', :git => 'https://github.com/NIFTYCloud-mbaas/ncmb_ios.git' end
|
ターミナルに戻り下記コマンドでNCMBのライブラリをダウンロードします。
このように表示されればOKです。

一度Xcodeプロジェクトを閉じて、下図のとおりxcworkspace
からXcodeを開き直してください。

NCMB初期設定
次に、NCMBの初期設定をおこないます。
NCMBはObjective-Cという言語で作られており、Swiftで利用する為には、Objective-CからSwiftへ橋渡しする為のファイル(Bridging-Header.h)を作成し、Swiftで扱えるように設定する必要があります。
まずは橋渡しする為のファイル(Bridging-Header.h)を作成しましょう。
プロジェクトの「PhotoShare」フォルダーで右クリックをし、「New Files..」を選択します。

「Header File」を選択し、「Next」を押します。

Save Asに「PhotoShare-Bridging-Header.h」と入力して「Create」ボタンを押します。

「PhotoShare-Bridging-Header.h」を以下へ編集します。
PhotoShare-Bridging-Header.h

Xcodeへ「PhotoShare-Bridging-Header.h」を読み込ませる為の設定をします。
「PhotoShare」プロジェクトの「Build Settings」の「Objective-C Bridging Header」に「PhotoShare/PhotoShare-Bridging-Header.h」を指定します。

続いて、下記のようにAppDelegate
にimport NCMB
を追加し、NCMB.setApplicationKey
メソッドの引数に、NCMBのアプリ作成で生成されたアプリケーションキーとクライアントキーを設定します。
AppDelegate.swift
import UIKit
import NCMB
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { NCMB.setApplicationKey("【アプリケーションキー】", clientKey: "【クライアントキー】") return true }
|
これでNCMBと連携する準備は完了です。
ログイン・会員登録機能実装
画面デザイン
Main.storyboard
を開いて、下図のとおりViewController
を選択してEditor -> Embed In -> Navigation Controller
を選択してください。

そうすると下図のとおりViewController
の前にNavigation Controller
が追加されます。
(画面サイズは見やすくするために4インチにしておきましょう。)

下図を参考にログイン画面・会員登録画面を作ってください。
ViewControllerを追加したので、下表のとおりにViewControllerのswiftファイルを作成して関連付けてください。
画面名 |
swiftファイル名 |
ログイン画面 |
LoginViewController.swift |
ユーザ登録画面 |
UserRegisterViewController.swift |
ユーザID、パスワード入力用のTextField
、ログイン、新規ユーザ登録、新規登録のアクションができるようにButton
を配置してください。

ViewController
からログイン画面にSegue(Show)
を追加します。

Segue
のidentifier
をtoLogin
にします。

次にログイン画面の新規ユーザ登録
ボタンからユーザ登録画面でSegue(Show)
を追加し、identifier
はtoRegister
にしてください。

ログイン画面のナビゲーション部分にタイトルを入れます。下図のようにNavigation Item
をナビゲーションバーの部分にドラッグすればタイトルを設定できるようになります。

ユーザ登録画面のナビゲーション部分にタイトルを入れます。下図のようにNavigation Item
をナビゲーションバーの部分にドラッグすればタイトルを設定できるようになります。

IBAutlet
、IBAction
も下図のとおりに設定してください。


ログインチェック
最初にViewController
にてログイン状況を判断し、まだログインしていなければLoginViewController
に遷移するようにします。
ViewController.swift
import UIKit
import NCMB
class ViewController: UIViewController { : : override func viewDidLoad() { super.viewDidLoad() }
override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) self.loginCheck() }
func loginCheck(){ if (NCMBUser.currentUser() != nil) { print("ログイン済み") } else { print("未ログイン") self.performSegueWithIdentifier("toLogin", sender: nil) } }
|
この状態でRunしてシュミレータで確認してみましょう。まだログインしていませんので、起動後すぐにログイン画面へ遷移すればOKです。

会員登録機能実装
次にユーザ登録画面にてユーザ登録する処理を追加します。UserRegisterViewController
のIBAction
のuserRegisterButtonTapped
メソッドの箇所にNCMBにユーザ登録する処理を追加します。
UserRegisterViewController.swift
import UIKit
import NCMB
class UserRegisterViewController: UIViewController { : : : @IBAction func userRegisterButtonTapped(sender: AnyObject) { let user = NCMBUser() user.userName = self.userIdTextField.text user.password = self.passwordTextField.text let acl = NCMBACL() acl.setPublicReadAccess(true) user.ACL = acl user.signUpInBackgroundWithBlock({(error) in if (error != nil){ print("ユーザ登録失敗:\(error)") }else{ print("ユーザ登録完了") let alert = UIAlertController(title: "登録完了", message: "ユーザ登録完了しました。", preferredStyle: .Alert) alert.addAction(UIAlertAction( title: "OK", style: .Default, handler: {(action:UIAlertAction) -> Void in self.navigationController?.popToRootViewControllerAnimated(true) })) self.presentViewController(alert, animated: true, completion: nil) } }) } }
|
NCMBにユーザを作成する場合にはNCMBUser
インスタンスを生成します。
NCMBUser
インスタンスにユーザID(userName
)とパスワード(password
)をセットし、
さらにACL(アクセス権限)設定をセットし、signUpInBackgroundWithBlock
メソッドでユーザ登録を行います。
これですでにNCMBにてユーザ登録できるようになっています。Runを実行してユーザ登録してみましょう。ユーザID、パスワードはとりあえずともにuser1
でユーザを作成してください。

ユーザ作成完了すると下図のようにアラート画面が表示されるはずです。

NCMBの管理画面でユーザ登録しているか確認してみましょう。
NCMBにログインしてください。
アプリをMessenger
にして会員管理
の箇所をクリックします。
すると下図のようにuser1
のレコードが1行追加されていると思います。

このようにNCMBを使えば会員機能を簡単に実装する事ができます。
ログアウト機能実装
ユーザ作成が完了するとアプリはログイン状態になっています。
今回は擬似的に複数のユーザを作成したり、切り替えたりしたいので、まずログアウト機能を実装しておきます。
Main.storyboardを開き、ViewController
にBar Button Item
を追加し、IBAction
でlogoutButtonTapped
メソッドを追加します。
追加したlogoutButtonTapped
ボタンにログアウト機能を追加します。
ViewController.swift
func loginCheck(){ if (NCMBUser.currentUser() != nil) { print("ログイン済み") } else { print("未ログイン") self.performSegueWithIdentifier("toLogin", sender: nil) } }
@IBAction func logoutButtonTapped(sender: AnyObject) { NCMBUser.logOut() self.loginCheck() }
|
NCMBUser.logOut
でログアウトします。
ログアウトしたらまた自動的にログイン画面を表示するようにloginCheck
メソッドを実行しています。
Main.storyboardを開いて、ログアウトボタンを追加してIBAction
として関連付けしましょう。
また今回ViewControllerはタイムライン画面として使うのでナビゲーションバーのタイトルをタイムライン
と表記しておきましょう。

それではRunしてログアウトできるか確認しましょう。

ログイン機能実装
ユーザ登録できるようになりましたので、続いてログイン機能を実装していきましょう。
NCMBをimportし、LoginViewController
のIBAction
のloginButtonTapped
メソッドにログイン部分のコードを追加します。
LoginViewController.swift
import UIKit
import NCMB
class LoginViewController: UIViewController { : : @IBAction func loginButtonTapped(sender: AnyObject) { let userId = userIdTextField.text! let password = passwordTextField.text! NCMBUser.logInWithUsernameInBackground(userId, password: password, block:({(user: NCMBUser!, error: NSError!) in if (error != nil){ print("ログイン失敗:\(error)") }else{ print("ログイン成功:\(user)") self.navigationController?.popToRootViewControllerAnimated(true) } })) } }
|
画面にあるユーザIDとパスワードを入力するテキストフィールド(userIdTextField、passwordTextField)の値をNCMBUser.logInWithUsernameInBackgroundの引数にセットして実行する事によりNCMBに対してログインを実行します。
ユーザIDとパスワードが正しければ、user
の中にユーザ情報が格納されます。ログイン失敗時はerror
に値が入っていますので、if (error != nil)
で条件分岐して判断する事ができます。
それでは先ほど作成したuser1
でログインできるか確認してみましょう。
下記のようにデバッグエリアにログイン成功
と表示されればOKです。

ユーザを追加
今回のアプリは複数ユーザを想定したものですので、複数ユーザを予め作成しておきます。
会員登録、ログアウトを繰り返し、user2
、user3
でユーザID、パスワードでユーザを作成してください。
このようにNCMB管理画面の会員管理でユーザが作成されているか確認しておきましょう。

ユーザ一覧画面
画面レイアウト
続いてユーザ一覧画面を作っていきます。Main.storyboardを開き、TabbarControlを追加してUserListViewController
、UserDetailViewController
を作ってください。


NavigationControllerの下図の場所をダブルクリックするとタブバーのタイトルを変更できます。
タイムライン
にしておいてください。

新しいViewControllerを追加してTab Bar Controller
からcontrol
キーをクリックしながらドラッグしてください。

すると下図のように選択項目が出てきますのでview controller
を選択してください。

このように1つのタブコンテンツとして追加されます。

ユーザ
に変更しておきましょう。

UserListViewController.swift
のファイルを新規作成して追加したViewControllerに関連付けしてください。

TableView
を配置してPrototype Cell
を1
にしてください。

Cell
のIdentifier
をCell
にしてください。

次にこのViewControllerも階層を持たせたいのでEditor -> Embed In -> Navigation Controller
を選択してNavigation Controller
を追加してください。
また、ナビゲーションバーのタイトルはユーザ一覧
にしておいてください。

最後にIBOutlet
でTableView
を関連付けておきましょう。

ユーザ一覧表示
では実際にコードを追加してユーザ一覧を表示するようにしていきましょう。
UserListViewController.swift
import UIKit
import NCMB
class UserListViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var userList = NSArray() @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self tableView.dataSource = self } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) fetchUserList() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return userList.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) let user = userList.objectAtIndex(indexPath.row) as! NCMBUser cell.textLabel!.text = user.userName return cell }
func fetchUserList(){ let query = NCMBQuery(className: "user") query.whereKey("objectId", notEqualTo: NCMBUser.currentUser().objectId) query.findObjectsInBackgroundWithBlock({(objects, error) in if (error != nil){ print("友達取得失敗:\(error)") } else { print("友達取得成功") self.userList = objects self.tableView.reloadData() } }) } }
|
これでユーザ一覧が表示される事を確認してください。

ユーザ詳細画面
ユーザ一覧からユーザ詳細画面に遷移できるようにします。
画面デザイン
Main.storyboardを開いて、新しくViewControllerを追加して、下図のようにユーザ一覧画面とSegue(Show)
で接続し、SegueのIdentifier
はtoUserDetail
にします。

下図のようにNavigation item
とLabel
とButton
を配置してください。

UserDetailViewController.swift
でファイルを新規作成し、追加したViewControllerと関連づけてください。

IBOutlet
で下図のとおり関連付けしてください。

コード実装
前の画面からログインユーザであるNCMBUser
を受け取り、その情報を画面に表示させます。また、友達申請ボタンの初期設定を定義しています。
UserDetailViewController.swift
import UIKit
import NCMB
class UserDetailViewController: UIViewController { @IBOutlet weak var friendRequestedLabel: UILabel! @IBOutlet weak var userIdLabel: UILabel! @IBOutlet weak var friendRequestButton: UIButton!
var user = NCMBUser()
override func viewDidLoad() { super.viewDidLoad()
self.userIdLabel.text = user.userName self.friendRequestedLabel.hidden = true self.friendRequestButton.setTitle("友達申請", forState: .Normal) self.friendRequestButton.enabled = true self.friendRequestButton.addTarget(self, action: #selector(UserDetailViewController.sendFriendRequest(_:)), forControlEvents: .TouchUpInside)
}
|
前の画面(UserListViewController)から画面遷移するようにコードを追加します。
UserListViewController.swift
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let user = userList.objectAtIndex(indexPath.row) as! NCMBUser self.performSegueWithIdentifier("toUserDetail", sender: user) }
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "toUserDetail" { let vc = segue.destinationViewController as! UserDetailViewController vc.user = sender as! NCMBUser } }
|
一度Runして動作確認しておきましょう。ユーザ一覧画面から、ユーザ詳細画面へ表示でき、ユーザ名が表示される事を確認してください。

友達申請機能
友達申請機能を実装します。友達申請を管理するために、NCMBにFriendRequest
というテーブルを作って管理します。
FriendRequestテーブル構造
カラム名 |
説明 |
from |
友達承認リクエストしているユーザ(ログインユーザ) |
to |
友達承認リクエストされているユーザ(選択ユーザ) |
status |
友達承認ステータス
pending ・・・ログインユーザがリクエスト中(未承認)
requested ・・・ログインユーザが友達承認リクエストを受けている(かつ未承認)
accepted ・・・リクエストを承認(承認された)状態 |
UserDetailViewController.swift
@IBAction func sendFriendRequest(sender: AnyObject){ let fr = NCMBObject(className: "FriendRequest") fr.setObject(NCMBUser.currentUser(), forKey:"from" ) fr.setObject(self.user, forKey:"to" ) fr.setObject("pending", forKey: "status") fr.saveInBackgroundWithBlock({(NSError error) in if error != nil { print("友達申請保存失敗:\(error)") } else { print("友達申請保存成功:\(fr)") } }) }
|
この状態で一度Runして動作確認してみましょう。友達申請ボタンをタップすると、NCMB側にFriendRequest
テーブルが作成され、レコードが一行増えている事を確認しましょう。

申請状況を取得して画面表示を切り替え
登録した友達リクエストの申請・承認状況を取得して、それに応じて表示を切り替えるようにします。
UserDetailViewController.swift
override func viewDidLoad() { super.viewDidLoad() : : self.checkFriendRequestStatus() }
@IBAction func sendFriendRequest(sender: AnyObject){ let fr = NCMBObject(className: "FriendRequest") fr.setObject(NCMBUser.currentUser(), forKey:"from" ) fr.setObject(self.user, forKey:"to" ) fr.setObject("pending", forKey: "status") fr.saveInBackgroundWithBlock({(NSError error) in if error != nil { print("友達申請保存失敗:\(error)") } else { print("友達申請保存成功:\(fr)") self.changeDisplayByStatus(fr) } }) } : :
func checkFriendRequestStatus(){ let query = NCMBQuery(className: "FriendRequest") query.whereKey("from", equalTo: NCMBUser.currentUser()) query.whereKey("to", equalTo: self.user) query.findObjectsInBackgroundWithBlock({(objects, error) in if (error == nil) { print("登録件数: \(objects.count)") if objects.count > 0 { let fr = objects[0] as! NCMBObject self.changeDisplayByStatus(fr) } } else { print("友達申請状況取得失敗: \(error)") } }) }
func changeDisplayByStatus(friendRequest:NCMBObject){ let status = friendRequest.objectForKey("status") as! String switch status { case "pending": self.friendRequestButton.setTitle("友達申請中", forState: .Normal) self.friendRequestButton.enabled = false case "accepted": self.friendRequestButton.setTitle("友達承認済み", forState: .Normal) self.friendRequestButton.enabled = false self.friendRequestedLabel.hidden = true case "requested": self.friendRequestedLabel.hidden = false self.friendRequestButton.setTitle("承認", forState: .Normal) self.friendRequestButton.enabled = true self.friendRequestButton.removeTarget(self, action: #selector(UserDetailViewController.sendFriendRequest(_:)), forControlEvents: .TouchUpInside) self.friendRequestButton.addTarget(self, action: #selector(UserDetailViewController.acceptFriendRequest(_:)), forControlEvents: .TouchUpInside) default: self.friendRequestButton.setTitle("友達申請", forState: .Normal) self.friendRequestButton.enabled = true self.friendRequestButton.addTarget(self, action: #selector(UserDetailViewController.sendFriendRequest(_:)), forControlEvents: .TouchUpInside) } }
|
このようにNCMBのデータストアから、ログインユーザと選択ユーザを取得条件にしてFriendRequest
テーブルを検索してデータが取得できたら、そのデータ(NCMBObject
)のstatus
プロパティの値に応じてswitch-case
で条件分岐をしてボタンのラベル表記、ボタンタップ時に呼び出すメッソドを変更しています。
それではこの状態で一度Runしてみましょう。先程友達申請をしたユーザの詳細画面にアクセスすると申請ボタンが申請中
となっており、ボタンがタップできない状態になっている事が確認できればOKです。

友達申請を受けている状態をチェック
友達申請は、ログインユーザが選択ユーザに対して友達申請をしている状態(pending
)もあれば、選択ユーザから友達申請をリクエストされている状態(requested
)もあります。
選択ユーザから友達申請をリクエストされている状態をチェクする処理を追加します。
UserDetailViewController.swift
override func viewDidLoad() { super.viewDidLoad() : : self.checkFriendRequestStatus() self.checkFriendRequestedBySelectedUser() } : : :
func checkFriendRequestedBySelectedUser(){ let query = NCMBQuery(className: "FriendRequest") query.whereKey("from", equalTo: self.user) query.whereKey("to", equalTo: NCMBUser.currentUser()) query.findObjectsInBackgroundWithBlock({(objects, error) in if (error == nil) { if objects.count > 0 { print("objects:\(objects)") let fr = objects[0] as! NCMBObject if fr.objectForKey("status") as! String == "pending"{ fr.setObject("requested", forKey: "status") } self.changeDisplayByStatus(fr) } } else { print("友達申請状況取得失敗: \(error)") } }) }
|
この状態でRunして確認してみましょう。今後は申請されたユーザでログインしなおしてもらい、申請したユーザを選択すると下図のように申請されているメッセージが表示され、ボタンが「承認」になっている事を確認できるはずです。

友達申請を承認する
友達申請されている(requested
)されている場合には、承認できるようにしなければなりません。
承認するための処理を追加します。
UserDetailViewController.swift
@IBAction func acceptFriendRequest(sender:AnyObject){ let query = NCMBQuery(className: "FriendRequest") query.whereKey("from", equalTo: self.user) query.whereKey("to", equalTo: NCMBUser.currentUser()) query.findObjectsInBackgroundWithBlock({(objects, error) in if (error == nil) { print("登録件数: \(objects.count)") if objects.count > 0 { let fr = objects[0] as! NCMBObject fr.setObject("accepted", forKey: "status") fr.saveInBackgroundWithBlock({(error) in if error != nil { print("友達申請承認失敗:\(error)") } else { print("友達申請承認成功") let fr = NCMBObject(className: "FriendRequest") fr.setObject(NCMBUser.currentUser(), forKey:"from" ) fr.setObject(self.user, forKey:"to" ) fr.setObject("accepted", forKey: "status") fr.saveInBackgroundWithBlock({(error) in if error != nil { print("友達申請保存失敗:\(error)") } else { print("友達申請保存成功:\(fr)") self.changeDisplayByStatus(fr) } }) self.changeDisplayByStatus(fr) } }) } } else { print("友達申請状況取得失敗: \(error)") } }) }
|
上記のコードでは下記の処理を実行しています。
- 条件を
from
を選択ユーザ、to
をログインユーザにして取得
- 取得したデータのstatusを
accepted
にして保存
- from,toを逆さにしたデータを作り保存
承認される事により、承認した側も、承認された側としてFriendRequestレコードを作成し、ともにaccepted
状態にする事で承認状態としています。
これをすることにより、承認されたユーザ側もaccepted
状態になり辻褄があるようになります。
この状態で実行してみましょう。承認ボタンをタップすると下図のように表示され、NCMBの管理画面にもaccepted
のレコードが2件登録されている事が確認できます。


写真投稿画面
次に写真を投稿する画面を構築していきます。
画面デザイン
タブを一つ追加して、写真取得先選択画面(ImagePickerViewController
)と、写真投稿(ImagePostViewController
)を追加していきます。
新しいViewControllerを追加してTab Bar Controller
からcontrol
キーをクリックしながらドラッグしてください。

すると下図のように選択項目が出てきますのでview controller
を選択してください。

このように1つのタブコンテンツとして追加されます。

写真
に変更しておきましょう。

ImagePickerViewController.swift
のファイルを新規作成して追加したViewControllerに関連付けしてください。

下図のようにButton
を2つ配置しましょう。

次にこのViewControllerも階層を持たせたいのでEditor -> Embed In -> Navigation Controller
を選択してNavigation Controller
を追加してください。
また、ナビゲーションバーのタイトルはユーザ一覧
にしておいてください。

下図のとおりIBAction
として関連付けしましょう。

新しいViewControllerを追加してSegue(Show)
で接続してIdentifier
をtoPostImage
にします。

ImagePostViewController.swift
のファイルを新規作成して追加したViewControllerに関連付けしてください。

下図のようにImageView
、Button
を配置してください。また、Navigation Item
をナビゲーションバーの部分に配置し、タイトルを写真投稿
にしてください。

下図のとおりIBOutlet
、IBAction
の関連付けしましょう。

UIImagePickerControllerを使った画像取得
それでは写真を投稿するためにカメラまたはアルバムから写真を取得できるようにコードを追加していきます。
ImagePickerViewController.swift
class ImagePickerViewController: UIViewController,UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var pickedImage = UIImage() : : @IBAction func pickImageFromCamera(sender: AnyObject) { if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) { let controller = UIImagePickerController() controller.delegate = self controller.sourceType = UIImagePickerControllerSourceType.Camera self.presentViewController(controller, animated: true, completion: nil) } } @IBAction func pickImageFromLibrary(sender: AnyObject) { if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary) { let controller = UIImagePickerController() controller.delegate = self controller.sourceType = UIImagePickerControllerSourceType.PhotoLibrary self.presentViewController(controller, animated: true, completion: nil) } } func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { if info[UIImagePickerControllerOriginalImage] != nil { pickedImage = info[UIImagePickerControllerOriginalImage] as! UIImage performSegueWithIdentifier("toPostImage", sender: nil) } picker.dismissViewControllerAnimated(true, completion: nil) } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "toPostImage" { let vc = segue.destinationViewController as! ImagePostViewController vc.pickedImage = self.pickedImage } } }
|
vc.pickedImage = self.pickedImage
の箇所でエラーがでますが、次の実装でエラーは解消されますので、次に進んでください。
画像投稿
次に画像投稿画面の処理を追加していきます。
まずは、前の画面で選択された画像を画面に表示させます。
ImagePostViewController.swift
import UIKit
import NCMB
class ImagePostViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView! var pickedImage:UIImage! var imageFile:NCMBFile! override func viewDidLoad() { super.viewDidLoad() }
override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) self.imageView.image = pickedImage self.imageView.contentMode = UIViewContentMode.ScaleAspectFit } : : }
|
この状態でRunして確認しましょう。選択した画像が次の画面で表示されている事を確認してください。
※ シュミレータの場合はカメラは使えませんのでアルバムを使って写真を取得しましょう。

次に画像をNCMBに保存する処理を追加します。
ImagePostViewController.swift
@IBAction func postButtonTapped(sender: AnyObject) { postImageToNCMB() }
func postImageToNCMB(){ let resizedImage = self.resizeImage(pickedImage, ratio: 0.1) let imageData = NSData(data: UIImagePNGRepresentation(resizedImage)!) as NSData let filename = "\(NSUUID().UUIDString).png" imageFile = NCMBFile.fileWithName(filename, data:imageData) as! NCMBFile UIApplication.sharedApplication().networkActivityIndicatorVisible = true imageFile.saveInBackgroundWithBlock({(error) in UIApplication.sharedApplication().networkActivityIndicatorVisible = false if (error != nil) { print("写真保存失敗:\(error)") } else { print("写真保存成功") let imageMeta = NCMBObject(className: "Photo") imageMeta.setObject(filename, forKey: "filename") imageMeta.setObject(NCMBUser.currentUser(), forKey: "owner") imageMeta.saveInBackgroundWithBlock({(error) in if (error != nil) { print("写真メタ保存失敗:\(error)") }else{ print("写真メタ保存成功") let alert = UIAlertController( title: "写真アップロード完了", message: "写真のアップロード完了しました。", preferredStyle: .Alert ) let action = UIAlertAction( title: "OK", style: .Default, handler: {(action:UIAlertAction) -> Void in self.navigationController?.popToRootViewControllerAnimated(true) }) alert.addAction(action) self.presentViewController(alert, animated: true, completion: nil) } }) } },progressBlock: { (percentDone: Int32) -> Void in print("upload status:\(percentDone)%") }) }
func resizeImage(image: UIImage, ratio: CGFloat) -> UIImage { let size = CGSizeMake(image.size.width * ratio, image.size.height * ratio) UIGraphicsBeginImageContext(size) image.drawInRect(CGRectMake(0, 0, size.width, size.height)) let resizedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return resizedImage }
|
NCMBにファイル(今回が画像)をアップロードする場合にはNCMBFile
を使ってアップロードします。ファイル名はユニークな値にしないといけないので、今回はUUID
を生成してそれをファイル名にしています。
ファイルのアップロード完了時には、そのファイル名と投稿ユーザをデータストア(今回はPhoto
テーブル)に保存しています。
それでは一度Runしてみましょう。投稿完了したらアラート画面が表示されます。

またNCMB管理画面ではPhoto
テーブルが作成されてレコードが追加されています。

画像ファイルもアップロードされています。ファイルストア
の中に画像ファイルがアップロードされている事が確認できます。

タイムライン画面
それでは次に、投稿した写真を閲覧できるようタイムライン画面を構築していきます。
タイムラインが下記の使用で作成していきます。
- CollectionViewを利用
- 自分と友達でかつ公開範囲が友達までの写真を表示
- いいねボタンを追加
画面レイアウト
Main.storyboardを開いて、タイムライン画面にCollectionView
を画面全体に表示させます。

IBOutlet
の関連付けもしておきましょう。

カスタムコレクションビューセルの作成
今回はコレクションビューの一つのセルのレイアウトをカスタマイズして表示していきます。
File -> Newでカスタムビューを作成します。
項目 |
値 |
Class |
TimeLineCollectionViewCell |
Subclass of |
UICollectionViewCell |
Also create XIB file |
チェックする |
Language |
Swift |

下図のように2つのファイルが作成されます。

TimeLineCollectionViewCell.xib
を開いて下図のようにレイアウトしていきます。
まずはサイズを下図のとおり横幅320、縦幅390にしてください。

下図のようにImageView
、Label
、Button
を配置してください。ImageView
は320 x 320の正方形にしてください。

Time Line Collection View Cell
の backgroundColor は white Color にしてください。

下図のとおりにIBOutlet
の関連付けをしてください。

画像データ取得
それではタイムライン画面で画像データ、画像ファイルをNCMBから取得して画面に表示させる処理を追加していきます。
ViewController.swift
import UIKit import NCMB class ViewController: UIViewController{ @IBOutlet weak var collectionView: UICollectionView! var photos = NSMutableArray() : : func loginCheck(){ if (NCMBUser.currentUser() != nil) { print("ログイン済み") self.fetchPhotos() } else { print("未ログイン") self.performSegueWithIdentifier("toLogin", sender: nil) } } func fetchPhotos(){ photos = NSMutableArray() let friendQuery = NCMBQuery(className: "FriendRequest") friendQuery.whereKey("from", equalTo: NCMBUser.currentUser()) friendQuery.whereKey("status", equalTo: "accepted") let photoQueryA = NCMBQuery(className: "Photo") photoQueryA.whereKey("owner", matchesKey: "to", inQuery: friendQuery) let photoQueryB = NCMBQuery(className: "Photo") photoQueryB.whereKey("owner", equalTo: NCMBUser.currentUser()) let query = NCMBQuery.orQueryWithSubqueries([photoQueryA,photoQueryB]) query.findObjectsInBackgroundWithBlock({(objects, error) in UIApplication.sharedApplication().networkActivityIndicatorVisible = false if (error == nil) { print("画像データ取得成功: \(objects)") for photo in objects { let filename = photo.objectForKey("filename") as! String! print("ObjectId: \(photo.objectId) filename: \(filename)") self.photos.addObject(photo) } self.collectionView?.reloadData() } else { print("画像データ取得失敗: \(error)") } }) } }
|
fetchPhotos
のメソッドの中では、下記のクエリを組み合わせて画像のデータを取得しています。
- クエリ1 FriendRequestテーブルから友達(fromがログインユーザの条件のもの)取得
- クエリ2 クエリ1の友達リストのtoのユーザがownerのPhotoデータを条件定義
- クエリ3 ログインユーザがownerのPhotoデータの条件定義
- クエリ4 クエリ2とクエリ3をOR条件でPhotoデータを取得
このクエリを組み合わせた結果として自分(ログインユーザ)もしくは友達ユーザの画像データを取得
する事が可能です。
この状態で一度Runしてタイムライン画面を開きましょう。デバッグエリアに画像データが表示していればOKです。

コレクションビューを使った画像表示
それでは次にコレクションビューを使って画像を表示させたいと思います。
ViewController.swift
//////////////// ▼▼ プロトコル追加 ▼▼ //////////////// class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout { //////////////// ▲▲ プロトコル追加 ▲▲ //////////////// : @IBOutlet weak var collectionView: UICollectionView! : override func viewDidLoad() { super.viewDidLoad() //////////////// ▼▼ 追加 ▼▼ //////////////// collectionView.delegate = self collectionView.dataSource = self // カスタムセルの登録 collectionView.registerNib(UINib(nibName: "TimeLineCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "TimeLineCollectionViewCell") //////////////// ▲▲ 追加 ▲▲ //////////////// } : : //////////////// ▼▼ 追加 ▼▼ //////////////// // MARK: - CollectionView Datasource // コレクションビューのセクションの数を定義(今回は1つ) func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { return 1 } // コレクションビューのアイテム数を定義(今回は取得画像分) func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return photos.count } // コレクションビューのアイテムの表示要素を定義 func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { // カスタムセルの利用 let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TimeLineCollectionViewCell", forIndexPath: indexPath) as! TimeLineCollectionViewCell // Photoデータから写真取得 let photo = photos.objectAtIndex(indexPath.row) as! NCMBObject // 画像投稿者を表示 let user = photo.objectForKey("owner") as! NCMBUser cell.userIdLabel.text = user.userName // 画像投稿日時の表示 cell.postDateLabel.text = photo.objectForKey("createDate") as? String // 画像ファイル取得してセルに表示 if cell.imageView?.image == nil { UIApplication.sharedApplication().networkActivityIndicatorVisible = true let fileData = NCMBFile.fileWithName(photo.objectForKey("filename") as! String, data: nil) as! NCMBFile fileData.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError!) -> Void in UIApplication.sharedApplication().networkActivityIndicatorVisible = false if error != nil { print("写真の取得失敗: \(error)") } else { cell.imageView?.image = UIImage(data: imageData!) cell.layoutSubviews() } } } return cell } // MARK: - CollectionView FlowLayout // コレクションビューのアイテムのサイズを定義 func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { // 横幅は画面いっぱい、縦幅は横幅 + 70 let size = self.view.frame.width return CGSize(width: size, height: size + 70) } //////////////// ▲▲ 追加 ▲▲ //////////////// : : }
|
コレクションビューも TableView と同じようにUICollectionViewDataSource
、UICollectionViewDelegate
を追加して、コレクションビューのセクション数、アイテム数を定義します。さらにコレクションビューでは、UICollectionViewDelegateFlowLayout
のプロトコルを追加し、アイテムのサイズを定義する事が可能です。
また、コレクションビューのアイテムを今回カスタムビューで表示するために、viewDidLoad
の箇所にcollectionView.registerNib
を実行し、利用したカスタムビューのNib(Xib)を指定し、identifierをTimeLineCollectionViewCell
とし、collectionView.cellForItemAtIndexPath
で呼び出せるようにしています。
これで動作確認してみましょう。タイムライン画面に投稿した写真が表示されればOKです。

いいね機能追加
コレクションビューアイテムの「いいね」ボタンをタップしたときに、いいね数をカウントできるようにします。いいね数をカウントするためにNCMBにてLike
のテーブルを作成して管理していきます。
Likeテーブル定義
カラム名 |
説明 |
user |
いいねしたユーザ |
photo |
いいねした画像 |
ViewController.swift
func checkButtonTapped(sender:UIButton) { let buttonPosition = sender.convertPoint(CGPointZero, toView: self.collectionView) let indexPath = self.collectionView.indexPathForItemAtPoint(buttonPosition) if indexPath != nil { print(indexPath!.row) let photo = photos.objectAtIndex(indexPath!.row) as! NCMBObject addLike(photo,index: indexPath!) } }
func addLike(photo:NCMBObject,index:NSIndexPath){ let like = NCMBObject(className: "Like") like.setObject(NCMBUser.currentUser(), forKey: "user") like.setObject(photo, forKey: "photo") like.saveInBackgroundWithBlock({(error) in if (error != nil) { print("いいね保存失敗:\(error)") }else{ print("いいね保存成功") let cell = self.collectionView.cellForItemAtIndexPath(index) as! TimeLineCollectionViewCell cell.likeButton.enabled = false self.fetchNumberOfLike(photo, index: index) } }) }
func fetchNumberOfLike(photo:NCMBObject,index:NSIndexPath){ let query = NCMBQuery(className: "Like") query.whereKey("photo", equalTo: photo) query.findObjectsInBackgroundWithBlock({(objects, error) in if (error == nil) { print("いいね数取得成功: \(objects.count)") let cell = self.collectionView.cellForItemAtIndexPath(index) as! TimeLineCollectionViewCell cell.likeLabel.text = "いいね \(objects.count)人" } else { print("いいね数取得失敗: \(error)") } }) }
|
ViewController.swift
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { : : : fetchNumberOfLike(photo,index:indexPath) cell.likeButton.addTarget(self, action: #selector(ViewController.checkButtonTapped(_:)), forControlEvents: .TouchUpInside) return cell }
|
これで実行してみましょう。いいねボタンをタップしたら、いいね数がカウントアップされる事が確認できます。
