質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

5回答

2820閲覧

Swift3でMapViewのPinを縮小した写真にしたい

Robokun

総合スコア53

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2017/05/16 10:00

MapViewを使い、現在地で撮った写真をPinの画像にするフォトアルバムを作ろうとしています。
pickerViewで写真を撮った後に画像の配列に写真を入れて、それをPinの画像にしようとしています。
コードは以下のようにしました。
PickerViewを起動し、写真を撮るまではOKなのですが、撮り終わった後に

fatal error: Index out of range
というエラーメッセージで落ちてしまいます。
おそらく画像をUIimageの配列に入れる処理が悪いのだと思うのですが
解決方法が分かりません。
修正するヒントなどありましたら教えていただければと思います。

MapViewController.swift

1import UIKit 2import CoreLocation 3import MapKit 4 5class MapViewController: UIViewController { 6 7 @IBOutlet weak var mapView: MKMapView! 8 @IBOutlet weak var locationLabel: UILabel! 9 10 var locationManager = CLLocationManager() 11 var locationData:CLLocation? 12 var imageArray: Array<UIImage> = Array<UIImage>() 13 14 override func viewDidLoad() { 15 super.viewDidLoad() 16 17 //ロケーションの状態表示を使用不可にしておく 18 disableLocationLabel() 19 20 locationManager.desiredAccuracy = kCLLocationAccuracyBest 21 locationManager.distanceFilter = 1.0 22 mapView.setUserTrackingMode(.follow, animated: true) 23 24 locationManager.delegate = self 25 mapView.delegate = self 26 mapView.showsScale=true 27 } 28 29 override func didReceiveMemoryWarning() { 30 super.didReceiveMemoryWarning() 31 } 32 33 //画面呼び出し後のメソッド 34 override func viewDidAppear(_ animated: Bool) { 35 36 if(CLLocationManager.locationServicesEnabled() == true){ 37 switch CLLocationManager.authorizationStatus() { 38 39 //未設定の場合 40 case CLAuthorizationStatus.notDetermined: 41 locationManager.requestWhenInUseAuthorization() 42 43 //機能制限されている場合 44 case CLAuthorizationStatus.restricted: 45 alertMessage(message: "位置情報サービスの利用が制限されている利用できません。「設定」⇒「一般」⇒「機能制限」") 46 47 //「許可しない」に設定されている場合 48 case CLAuthorizationStatus.denied: 49 alertMessage(message: "位置情報の利用が許可されていないため利用できません。「設定」⇒「プライバシー」⇒「位置情報サービス」⇒「アプリ名」") 50 51 //「このAppの使用中のみ許可」に設定されている場合 52 case CLAuthorizationStatus.authorizedWhenInUse: 53 //位置情報の取得を開始する。 54 locationManager.startUpdatingLocation() 55 56 //「常に許可」に設定されている場合 57 case CLAuthorizationStatus.authorizedAlways: 58 //位置情報の取得を開始する。 59 locationManager.startUpdatingLocation() 60 } 61 62 } else { 63 //位置情報サービスがOFFの場合 64 alertMessage(message: "位置情報サービスがONになっていないため利用できません。「設定」⇒「プライバシー」⇒「位置情報サービス」") 65 } 66 } 67 68 //表示を使用不可に変更 69 func disableLocationLabel(){ 70 locationLabel.text = "GPSが使用許可になっていません" 71 } 72 73 //警告アラートを表示 74 func alertMessage(message:String) { 75 let aleartController = UIAlertController(title: "注意", message: message, preferredStyle: .alert) 76 let defaultAction = UIAlertAction(title:"OK", style: .default, handler:nil) 77 aleartController.addAction(defaultAction) 78 79 present(aleartController, animated:true, completion:nil) 80 } 81 82 //写真ボタンを押した時 83 @IBAction func takePhoto(_ sender: UIButton) { 84 85 //カメラが使えるか確認する 86 if UIImagePickerController.isSourceTypeAvailable(.camera){ 87 //ImagePickerを表示する 88 let cameraPicker = UIImagePickerController() 89 cameraPicker.sourceType = UIImagePickerControllerSourceType.camera 90 cameraPicker.delegate = self 91 self.present(cameraPicker, animated: true, completion: nil) 92 } 93 } 94 95 //戻るボタンを押した時 96 @IBAction func backButton(_ sender: UIButton) { 97 self.dismiss(animated: true, completion: nil) 98 } 99} 100 101//MARK: CLLocationManagerDelegate 102extension MapViewController:CLLocationManagerDelegate{ 103 104 //位置情報が更新された時に呼ばれる 105 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { 106 107 //Locationの最後の値を取り出す 108 locationData = locations.last 109 110 //緯度 111 if var ido = locationData?.coordinate.latitude{ 112 ido = round(ido * 1000000)/1000000 113 } 114 //経度 115 if var keido = locationData?.coordinate.longitude{ 116 keido = round(keido * 1000000)/1000000 117 } 118 //標高 119 if var hyoukou = locationData?.altitude{ 120 hyoukou = round(hyoukou*100)/100 121 } 122 123 //MAP中央の座標 124 let center = CLLocationCoordinate2D(latitude: (locationData?.coordinate.latitude)!, longitude: (locationData?.coordinate.longitude)!) 125 126 //地図のセンターに設定する 127 mapView.setCenter(center, animated: true) 128 129 //ロケーション表示(デバッグ用) 130 locationLabel.text = "GPS:\(center.latitude) \(center.longitude)" 131 132 //500m四方を表示する 133 let myRegion : MKCoordinateRegion = MKCoordinateRegionMakeWithDistance(center, 500, 500) 134 mapView.setRegion(myRegion, animated: true) 135 136 } 137 138 //位置情報のステータスが変わった時 139 func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { 140 141 switch status{ 142 //利用許可に変更された 143 case .authorizedAlways,.authorizedWhenInUse: 144 locationManager.startUpdatingLocation() 145 //利用不可に変更された 146 case .denied: 147 locationManager.stopUpdatingLocation() 148 disableLocationLabel() 149 150 default://制限されているか、ユーザーが決定していない、その他 151 //ロケーションの更新を停止する 152 locationManager.stopUpdatingLocation() 153 154 //トラッキングモードをnoneにする 155 mapView.setUserTrackingMode(.none, animated: true) 156 disableLocationLabel() 157 } 158 } 159} 160 161//MapViewのDelegate 162extension MapViewController:MKMapViewDelegate{ 163 164 //ピンを表示する時の処理 165 func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { 166 167 // 現在地は外す 168 if annotation is MKUserLocation { 169 return nil 170 } 171 172 let pinID = "PIN" 173 var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pinID) as? MKPinAnnotationView 174 //Annotationが見つからなかった場合は新しく作成 175 if annotationView == nil { 176 annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: pinID) 177 178 let size: CGSize = CGSize(width: 42, height: 42) 179 180 //写真を描画する 181 UIGraphicsBeginImageContext(size) 182 183 imageArray[Int(annotation.title!!)!].draw(in: CGRect(x:0, y:0, width : size.width,height: size.height)) 184 let resizeImage = UIGraphicsGetImageFromCurrentImageContext() 185 UIGraphicsEndImageContext() 186 187 annotationView?.image = resizeImage 188 189 //タップした時に吹き出しが表示されるようにする 190 annotationView?.canShowCallout = true 191 annotationView?.rightCalloutAccessoryView = UIButton(type: UIButtonType.detailDisclosure) 192 193 } else { 194 annotationView!.annotation = annotation 195 } 196 return annotationView 197 } 198 199 //吹き出しをタップした時に呼ばれる 200 //画像を表示するように遷移する 201 func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) { 202 //画面遷移 203 204 //Annotationを非選択にしてColloutを非表示にする 205 mapView.deselectAnnotation(view.annotation, animated: true) 206 } 207} 208 209//Pickerコントローラー 210extension MapViewController:UIImagePickerControllerDelegate{ 211 //写真を撮り終わった後の処理 212 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { 213 if let pickerImage = info[UIImagePickerControllerOriginalImage] as? UIImage{ 214 //画像用の配列に入れる 215 imageArray.append(pickerImage) 216 } 217 218 self.dismiss(animated: true, completion: nil) 219 220 //最後に取得した位置でCLLocationCoordinate2Dを作成する 221 let center : CLLocationCoordinate2D = CLLocationCoordinate2DMake((locationData?.coordinate.latitude)!, (locationData?.coordinate.longitude)!) 222 mapView.setCenter(center, animated: true) 223 224 225 let pin : MKPointAnnotation = MKPointAnnotation() 226 pin.coordinate = center 227 pin.title = "\(mapView.annotations.count)" 228 pin.subtitle = "\(String(describing: locationData?.coordinate.latitude )),\(String(describing: locationData?.coordinate.longitude))" 229 mapView.addAnnotation(pin) 230 } 231 232 //Pickerイメージでキャンセルされた時 233 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 234 self.dismiss(animated: true, completion: nil) 235 } 236} 237 238// 239extension MapViewController:UINavigationControllerDelegate{ 240 241} 242 243

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

解決済みですが、少し気になったことを書かせていただきます。

まず、アノテーションの生成は、pickerImageのif文の中に入れた方がいいと思います。
現在のコードだと、画像の取得に失敗したときにアノテーションと画像の数がズレてしまいます。
(失敗することがあるのかどうかは知りませんが‥)

pin.titleに入れる番号はimageArray.countを使うようにすれば「-1」という謎の補正が不要になります。
ただし、処理順をアノテーション生成 → 画像保存の順に変更しないといけません。

swift

1func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { 2 3 self.dismiss(animated: true, completion: nil) 4 5 if let pickerImage = info[UIImagePickerControllerOriginalImage] as? UIImage{ 6 7 //まずアノテーションを生成 8 //最後に取得した位置でCLLocationCoordinate2Dを作成する 9 let center : CLLocationCoordinate2D = CLLocationCoordinate2DMake((locationData?.coordinate.latitude)!, (locationData?.coordinate.longitude)!) 10 mapView.setCenter(center, animated: true) 11 12 let pin : MKPointAnnotation = MKPointAnnotation() 13 pin.coordinate = center 14 pin.title = "\(imageArray.count)" //※修正 15 pin.subtitle = "\(String(describing: locationData?.coordinate.latitude )),\(String(describing: locationData?.coordinate.longitude))" 16 mapView.addAnnotation(pin) 17 18 //画像用の配列に入れる 19 imageArray.append(pickerImage) 20 21 } else { 22 print("nil") 23 } 24}

投稿2017/05/18 05:35

fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ピンを表示する時の処理を以下のように変更しました。

Swift

1 //ピンを表示する時の処理 2 func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { 3 4 // 現在地は外す 5 if annotation is MKUserLocation { 6 return nil 7 } 8 9 let pinID = "PIN" 10 let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: pinID) 11 12 //var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pinID) as? MKPinAnnotationView 13 let size: CGSize = CGSize(width: 42, height: 42) 14 15 //写真を描画する 16 UIGraphicsBeginImageContext(size) 17 imageArray[Int(annotation.title!!)!].draw(in: CGRect(x:0, y:0, width : size.width,height: size.height)) 18 19 let resizeImage = UIGraphicsGetImageFromCurrentImageContext() 20 UIGraphicsEndImageContext() 21 22 annotationView.annotation = annotation 23 annotationView.image = resizeImage 24 25 //タップした時に吹き出しが表示されるようにする 26 annotationView.canShowCallout = true 27 //吹き出しにボタンをつける 28 annotationView.rightCalloutAccessoryView = UIButton(type: UIButtonType.detailDisclosure) 29 30 return annotationView 31 }

同時にPickerControlerの処理を以下のように変更しました

Swift

1 //写真を撮り終わった後の処理 2 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { 3 4 self.dismiss(animated: true, completion: nil) 5 6 //画像が作成できた場合 アノテーション作成 画像保存 7 if let pickerImage = info[UIImagePickerControllerOriginalImage] as? UIImage{ 8 //アノテーション作成 9 //最後に取得した位置でCLLocationCoordinate2Dを作成する 10 let center : CLLocationCoordinate2D = CLLocationCoordinate2DMake((locationData?.coordinate.latitude)!, (locationData?.coordinate.longitude)!) 11 mapView.setCenter(center, animated: true) //地図のセンターに設定する 12 let pin : MKPointAnnotation = MKPointAnnotation() //ピンを作成する 13 pin.coordinate = center //最後に取得した位置情報を設定する 14 pin.title = "\(imageArray.count)" 15 16 pin.subtitle = "\(String(describing: locationData?.coordinate.latitude )),\(String(describing: locationData?.coordinate.longitude))" 17 //地図にピンを立てる 18 mapView.addAnnotation(pin) 19 //画像用の配列に入れる 20 imageArray.append(pickerImage) 21 }else{ 22 print("画像作成に失敗した") 23 } 24 25 }

投稿2017/05/18 11:47

Robokun

総合スコア53

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

imageArray[ Int(annotation.title!!)! - 1].draw(in: CGRect(x:0, y:0, width : size.width,height: size.height))
と記述を変更したらエラーは出なくなりました。
ただ、写真は表示できていないままです

投稿2017/05/18 05:35

Robokun

総合スコア53

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2017/05/18 05:54

画像を表示するにはMKAnnotationViewを使いますので、 var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pinID) と annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: pinID) に変更すれば大丈夫かと。
Bongo

2017/05/18 06:13

画像表示部分関連で気になったのですが、質問者さんご提示のコードですと、アノテーションビューを新規生成した場合は大丈夫そうですが、既存のアノテーションビューを取得した場合にはビューに画像を設定する部分がスキップされてしまうのではないかと思いました。このあたりの挙動はどうなるのでしょう?
fuzzball

2017/05/18 06:19

するどいw 画像もそのまま使い回されてしまうので、おそらく質問者さんの意図した動作にはならないと思います。
Robokun

2017/05/18 11:43

ご指摘ありがとうございます。 func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? この部分のコードを変更して 意図した動きになりました。 解決方法に修正したコードを載せて起きます
guest

0

ベストアンサー

imagePickerControllerの中で画像をimageArrayに追加し、併せてMKPointAnnotationを追加しており、画像の数とMKPointAnnotationの数が常に一致することを前提としている、ということでしょうか。
となると、もし両者の不一致があればimageArrayのアクセス時にインデックスの不整合が発生しうると思われます。
imagePickerControllerの当該部分周辺にブレークポイントを仕掛けるなどして、ご確認されるとよさそうです。

投稿2017/05/18 02:08

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Robokun

2017/05/18 05:34 編集

ピンのTitleに振った番号で写真を読み出していたのですが、 ご指摘のようにimageArrayとのIndexとの不整合が起きていました。 imageArray[ Int(annotation.title!!)! - 1].draw(in: CGRect(x:0, y:0, width : size.width,height: size.height)) というように修正しました。 ただ、PINの画像が表示されないという症状は残ってしまいました ただ、質問が分かりづらくなるので 新たな質問でその点はお尋ねしようと思います ありがとうございました
guest

0

・修正するヒント

<当該エラー発生ステップ(行)の特定>
・エラー時にスタックトレースを確認
・ステップ実行するとか、怪しいところにログ出力をさせてみるなどを行い、どの部分で当該エラーが出ているのかを絞り込みます

<エラー発生ステップ内容の確認>
・特定できない場合
特定の仕方が悪い可能性が高い、想定する場所とは違うところでエラーになっている可能性を疑ってみる

・エラーが発生しているステップを特定できたのであれば、そのステップが想定通りの動作をしているのか、変数は想定通りの値になっているのかなどを確認します。

・エラー内容から、配列のインデックス(添え字)の指定した値が、想定より大きい値になっている可能性があるので、その点を重点的に確認するだけで良いかもしれませんが。

投稿2017/05/17 07:37

t_obara

総合スコア5488

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問