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/
Copyright © ITmedia, Inc. All Rights Reserved.