Optional型の説明が長くなってしまいましたが、ここで再びViewController.swiftファイルの実装に戻ります。
「prepareAnnotations」メソッドの内容は以下の通りでした。
private func prepareAnnotations() {
let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: nil)
fetchResult?.enumerateObjectsUsingBlock ({result, index, stop in
if let asset = result as? PHAsset {
if let location = asset.location {
let annotation = MKPointAnnotation()
annotation.coordinate = location.coordinate
self.mapView.addAnnotation(annotation)
}
}
})
}
3〜11行目の「fetchResult?.enumerateObjectsUsingBlock ({ ... })」では「Optional Chaining」を使用しています。「Optional Chaining」は「Optional型」や「Implicitly Unwrapped Optional型」の変数やメソッドに接尾辞「?」を付けて安全にアクセスできる仕組みです。
「Optional型」の変数に接尾辞「!」を付けて行うアンラップは、変数の内容にかかわらず中身を取り出すものでした。それに対し「Optional Chaining」は、変数が値を持つ場合は、その次の処理を継続し、変数の中身がnilの場合は処理を中断してnilを返します。
「prepareAnnotations」メソッド内の「fetchResult?.enumerateObjectsUsingBlock ({ ... })」の場合、fetchResultの中身がnilでない場合は、「enumerateObjectsUsingBlock」メソッドが実行され、fetchResultの中身がnilの場合は処理が終了します。
次に、PHFetchResultクラスのenumerateObjectsUsingBlockメソッドを見ていきます。
PHFetchResultオブジェクトは、フォトライブラリから取得した写真の情報を保持していて、enumerateObjectsUsingBlockメソッドを使用することで写真の情報を一つずつ取り出せます。
enumerateObjectsUsingBlockメソッドの定義は以下の通りです。
func enumerateObjectsUsingBlock(block: ((AnyObject!, Int, UnsafeMutablePointer<ObjCBool>) -> Void)!)
enumerateObjectsUsingBlockメソッドのblock引数は「クロージャ」になっています。クロージャは「値として扱える関数」のようなもので、メソッドの引数として渡すことができます。
enumerateObjectsUsingBlockメソッドに渡すクロージャ内には、写真情報一つを取り出した際に行う処理を記述しておきます。その処理は写真情報を取り出したときに実行されます。最終的に取り出せたデータ分の処理が行われます。
一つ目の引数resultに写真情報一つが入ってきます。resultの型はAnyObject!型(AnyObjectは任意のクラスのインスタンスを表す型)です。
クロージャ内のif文(4〜10行目)の条件式では、「型チェック」と「型キャスト」を行い「Optional Binding」を使用しています。最終的にPHAsset型のオブジェクトが取り出せた場合のみ真の処理が実行されます。
fetchResult?.enumerateObjectsUsingBlock ({result, index, stop in
if let asset = result as? PHAsset {
// PHAsset型のオブジェクトが取り出せた場合の処理
}
})
さらに内側のif文(5〜9行目)でも「Optional Binding」を使用しています。外側のif文で取り出したPHAsset型のオブジェクトから「CLLocation」型のオブジェクトを取り出し、注釈オブジェクトを作成して、MapViewへ追加しています。
これで、フォトライブラリから取得したPHAssetオブジェクトが保持する位置にピンが追加されます。
fetchResult?.enumerateObjectsUsingBlock ({result, index, stop in
if let asset = result as? PHAsset {
if let location = asset.location {
// CLLocation型のオブジェクトが取り出せた場合の処理
// 注釈オブジェクトを作成
let annotation = MKPointAnnotation()
annotation.coordinate = location.coordinate
// MapViewへ追加
self.mapView.addAnnotation(annotation)
}
}
})
途中で寄り道してしまったので、ViewController.swiftの全コードを載せておきます。
本記事の内容を実装していくと、ViewController.swiftのコードは以下のようになります。
import UIKit
import MapKit
import Photos
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.prepareMapView()
self.checkAuthorizationStatus()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func prepareMapView() {
self.mapView.rotateEnabled = false
self.mapView.pitchEnabled = false
let centerCoordinate = CLLocationCoordinate2D(latitude: 35.681382, longitude: 139.766084)
let initialSpan = MKCoordinateSpan(latitudeDelta: 0.4, longitudeDelta: 0.4)
let initialRegion = MKCoordinateRegion(center: centerCoordinate, span: initialSpan)
self.mapView.setRegion(initialRegion, animated: true)
}
private func checkAuthorizationStatus() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .Authorized:
self.prepareAnnotations()
default:
PHPhotoLibrary.requestAuthorization{ status in
if status == .Authorized {
self.prepareAnnotations()
}
}
}
}
private func prepareAnnotations() {
let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: nil)
fetchResult?.enumerateObjectsUsingBlock ({result, index, stop in
if let asset = result as? PHAsset {
if let location = asset.location {
let annotation = MKPointAnnotation()
annotation.coordinate = location.coordinate
self.mapView.addAnnotation(annotation)
}
}
})
}
}
RunボタンをクリックしてiOSシミュレーターで動かしてみましょう。
初回起動時の場合、フォトライブラリへのアクセスの許可を求めるアラートが表示されるので、「OK」をクリックします。
アラートが消えると、MapView上にピンが追加されるはず……ですが、「注釈オブジェクトの追加」と「ピンの描画」のタイミングがちょっと合ってないみたいです。MapViewを任意の方向へスワイプさせて、MapViewの表示範囲を少し変更してみてください。
MapViewの中心位置が移動すると、ピンが描画されるかと思います(この不具合は今後修正しましょう)。
今回は、前回の記事で追加した写真情報を「Photos Framework」の機能を使用して取り出し、写真情報にひも付いた座標にピンを表示しました。また、Swiftの特徴の一つである「Optional型」について解説しました。
次回も引き続き「PhotoMap」の実装を進めていきます。今回の記事で写真情報を基にピンを表示できたので、次回は実際の写真を取得し、標準のピンを写真のサムネイルに差し替えていきます。
クラスメソッド株式会社 iPhoneアプリサービス事業部所属のプログラマーです。iOSアプリの開発がメインですが、デザインやAWSなども勉強中です。
ブログ:http://dev.classmethod.jp/author/hiraya-shingo/
アプリは、どうやって動くの? プログラムって何?――初めてiPhoneアプリを作る人向け基礎知識まとめ
iOSアプリにフォトショの機能などを組み込めるCreative SDKの基礎知識とインストール
iPhoneアプリで位置情報と地図を使うための基礎知識
開発者視点で見る、あのドデカい「iPhone 6/6 Plus」
【図解】Xcode 4.5の使い方リファレンス超まとめ
Android化が進むiOS 7の新機能と開発環境Xcode 5Copyright © ITmedia, Inc. All Rights Reserved.