はじめに
アプリ開発を行った際、GoogleMapsを使用しましたが記事が少なく時間がかかった為備忘録として記録
目次
- インストール方法
- GoogleMapsを表示
- マーカーを表示(クラスター化)
- クラスタの画像を変更
- クラスタタップ時の処理
- マーカーの画像を変更
- マーカーの吹き出しを表示
- マーカーを消去
- マーカーを表示(単体表示)
- 現在地をマップに表示
- 現在地に遷移
- 縮尺度によって処理を行う
- GoogleMapsの地図の種類を変更
以下更新予定
- ストリートビューを実装
- ナイトモード実装
- フィチャータイプ
- パディング
- ポリゴン
- Jsonファイルの読み取り
環境
日付 : 2020年5月
MacOS : Catalina
Xcode Version : 11.41
cocoapods Version : 1.9.0.beta.3
インストール方法
step.1 新規プロジェクトを作成
プロジェクト名をGoogleMapsTest とした
step.2 ココアポッドをインストール(インストール済ならしなくてよい)
ターミナルに以下のコマンドを入力
sudo gem install cocoapods
pod setup
------memo------
pod --version
でバージョンの確認ができる
step.4 Podfile作成
ターミナルを開きファイルまでのパスを通す
cd <ファイルのパス>
podfileを作成
pod init
podfileが作成されたので、
以下2つのコードを追加
pod 'GoogleMaps'
pod 'GooglePlaces'
入力後以下のコードをターミナルで入力
pod install
一度Xcodeを終了させる。
finderから<プロジェクト名>.xcworkspaceを開く
------memo------
これで簡単にパスを通すことができる
cd <ファイルのパス> の<>は入力しなくて良い
GoogleMapsを表示
step.1 APIの設定
メニューバー(ハンバーガーメニュー)からAPIとサービス -> ライブラリ -> Maps SDK for iOSを選択
APIを有効にする
ここ書いてる記事少なくてつまづいた
次に
APIとサービス -> ライブラリ -> 認証情報から
認証情報を作成をタップするとAPIキーが作成され、キー制限をタップする
アプリケーションの制限からiOSアプリを選択
プロジェクトのバンドルIDを入力する。
step.2 マップ表示
XcodeのAppDelegate.swiftに以下のコードを追加
import UIKit
import GoogleMaps //追加
import GooglePlaces//追加
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
//ios12で動かす際に必須 場合に応じて入れてください
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//GoogleMapsを表示させるために認証キーを取得
GMSServices.provideAPIKey("<APIキー>")//追加
GMSPlacesClient.provideAPIKey("<APIキー>")//追加
return true
}
...以下省略
}
ViewControllerで以下のコードを入力
import UIKit
import GoogleMaps
class ViewController: UIViewController,GMSMapViewDelegate {
var latitude: Double = 34.077875549971 //徳島大学の緯度
var longitude: Double = 134.56156512254 //徳島大学の経度
override func loadView() {
super.loadView()
let camera = GMSCameraPosition.camera(withLatitude: latitude,
longitude: longitude,
zoom: 6.0)
let mapView = GMSMapView.map(withFrame: view.frame,
camera: camera)
view = mapView
}
}
------memo------
シュミレーターの言語設定を日本語にすることで、GoogleMapsを日本語表示にできる。
参考
https://blog.codecamp.jp/iphone-app-develope-original-googlemap
マーカーを表示(クラスター化)
podfileに以下を追加しpod installする
pod 'Google-Maps-iOS-Utils'
ViewControllerに以下のコードを記述
markerが4つ以上からクラスター化になる
import UIKit
import GoogleMaps
import GooglePlaces
import GoogleMapsUtils
class ViewController: UIViewController,GMSMapViewDelegate, GMUClusterManagerDelegate, GMUClusterRendererDelegate {
//[徳島大学,徳島県庁,地方裁判所,徳島駅]
var latitude: [Double] = [34.077875549971,34.06583,34.0708562,34.0742] //緯度
var longitude: [Double] = [134.56156512254,134.55944,134.5571692,134.5511] //経度
var zoom: Float = 8.0 //縮尺
var clusterManager: GMUClusterManager!
override func loadView() {
super.loadView()
let camera = GMSCameraPosition.camera(withLatitude: latitude[0],
longitude: longitude[0],
zoom: zoom)
let mapView = GMSMapView.map(withFrame: view.frame,
camera: camera)
view = mapView
//クラスタの画像について
let iconGenerator: GMUDefaultClusterIconGenerator!
iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
refresh()
}
func refresh(){
//クラスター全削除
self.clusterManager.clearItems()
for i in 0..<latitude.count{
let position = CLLocationCoordinate2DMake(latitude[i], longitude[i])
let item = POIItem(position: position)
self.clusterManager.add(item)
}
}
}
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
//緯度経度を保持
var position: CLLocationCoordinate2D
init(position: CLLocationCoordinate2D) {
self.position = position
}
}
クラスタの画像を変更
override func loadView() {
super.loadView()
//--追加--
//marker数[5, 10, 15, 20, 25]の順にimagesの画像が適応
let images: [UIImage] = [UIImage(named: "Assets内の画像名")!,
UIImage(named: "")!, UIImage(named: "")!, UIImage(named: "")!,UIImage(named: "")!]
iconGenerator = GMUDefaultClusterIconGenerator(buckets: [5, 10, 15, 20, 25], backgroundImages: images)
}
------memo------
右クリックからAddFilesを選び、画像を選択してAddする
そして、Assets内にドラッグアンドドロップする事で画像でまとめることができる
クラスタがタップされた時
今回はクラスタがタップされた時、ズームするようにした。
class ViewController: UIViewController,GMSMapViewDelegate, GMUClusterManagerDelegate, GMUClusterRendererDelegate {
var mapView: GMSMapView! //追加
override func viewDidLoad(){
super.viewDidLoad()
self.mapView = mapView//追加
}
//クラスタをタップされた時
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool{ //追加
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
return false
}
}
マーカーの画像を変更
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker){
//マーカーの位置を変更
marker.groundAnchor = CGPoint(x: 0.5, y: 1)
//クラスタ最下層の画像を変更
if let markerData = (marker.userData as? POIItem) {
marker.icon = UIImage(named: "camera")
}
}
マーカーの吹き出しを表示する
新しいViewControllerとxibファイルを作成
名前は
InfoViewController.swift
InfoView.xib
とした。
xibファイルのサイズはwidth:300 height:200にした
吹き出し画面はこのような感じにします。
import UIKit
class InfoViewController: UIView {
var view : UIView!
@IBOutlet weak var label1: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
}
func loadView() -> InfoViewController{
let seismographInfoView = Bundle.main.loadNibNamed("InfoView", owner: self, options: nil)?[0] as! InfoViewController
return seismographInfoView
}
func infoViewText(text1: String){
label1.text = text1
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
以下ViewControllerのコード全文です。
見辛くてすみません
import UIKit
import GoogleMaps
import GooglePlaces
import GoogleMapsUtils
class ViewController: UIViewController,GMSMapViewDelegate, GMUClusterManagerDelegate, GMUClusterRendererDelegate {
//[徳島大学,徳島県庁,地方裁判所,徳島駅]
var latitudes: [Double] = [34.077875549971,34.06583,34.0708562,34.0742] //緯度
var longitudes: [Double] = [134.56156512254,134.55944,134.5571692,134.5511] //経度
var texts:[String] = ["徳島大学","徳島県庁","地方裁判所","徳島駅"]
var zoom: Float = 13.0 //縮尺
var mapView: GMSMapView!
var clusterManager: GMUClusterManager!
var marker: GMSMarker? //追加
var infoViewController : InfoViewController? //追加
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withLatitude: latitudes[0],
longitude: longitudes[0],
zoom: zoom)
let mapView = GMSMapView.map(withFrame: view.frame,
camera: camera)
self.view = mapView
self.mapView = mapView
mapView.delegate = self
//クラスタの画像について
let iconGenerator: GMUDefaultClusterIconGenerator!
iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
refresh()
self.infoViewController = InfoViewController().loadView() //追加
}
func refresh(){
//クラスター全削除
self.clusterManager.clearItems()
for i in 0..<latitudes.count{
let position = CLLocationCoordinate2DMake(latitudes[i], longitudes[i])
let item = POIItem(position: position)
item.text = texts[i] //追加
self.clusterManager.add(item)
}
}
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker){
//マーカーの位置を変更
marker.groundAnchor = CGPoint(x: 0.5, y: 1)
//クラスタ最下層の画像を変更
if (marker.userData as? POIItem) != nil {
marker.icon = UIImage(named: "camera")
}
}
//クラスタをタップされた時
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool{
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
return false
}
// -------追加-------
//クラスタかmarkerをタップする時最初に呼ばれる
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
self.marker = marker
//markerをタップしたとき
guard let markerData = (marker.userData as? POIItem),
let infoViewController = infoViewController else {
return false
}
self.marker = marker
//透過度
infoViewController.layer.backgroundColor = UIColor(white: 1, alpha: 0.85).cgColor
//吹き出しの角を丸くする
infoViewController.layer.cornerRadius = 8
//吹き出しをピンの真上に出す
infoViewController.center = mapView.projection.point(for: marker.position)
infoViewController.center.y -= 140
infoViewController.infoViewText(text1: markerData.text)
mapView.addSubview(infoViewController)
return false
}
// -------追加-------
//画面変化時毎回呼ばれる
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
guard let marker = marker,
let infoViewController = infoViewController else{
return
}
infoViewController.center = mapView.projection.point(for: marker.position)
infoViewController.center.y -= 140
}
// -------追加-------
//吹き出しを消した時呼ばれる
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
infoViewController?.removeFromSuperview()
}
}
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
//緯度経度を保持
var position: CLLocationCoordinate2D
var text: String = "" //追加
init(position: CLLocationCoordinate2D) {
self.position = position
}
}
------memo------
新しいViewControllerとxibファイルの作り方はNewFileより
ViewControllerならCocoaTouchClass,xibファイルはViewを選択する
xibファイルのサイズ変更はFreeformにすることで変更可能
guardを使うことでオプショナル記号(!,?)を無くし美しいコードになる
またエラーが発見しやすい場合もしばしば。。。
参考
https://qiita.com/akidon0000/items/bf67f052179c1f704699
マーカーを消去
上のコードで記述済なのでそちらを見てちょ
self.clusterManager.clearItems()
マーカーを表示(単体表示)
https://qiita.com/akidon0000/items/bf67f052179c1f704699
このサイトを参照してください
昔に書いた記事なので稚拙なコードですが動くと思います
自分的には縮小した時の見やすさからクラスタ化がおすすめです。
現在地をマップに表示
Info.plistを編集します
今回はアプリ使用中のみ位置情報を取得するため、
Privacy - Location When In Use Usage Description を記述しました
常に位置情報を取得したい場合は
Privacy - Location Always and When In Use Usage Description を使用します
次にViewControllerに以下のコードを入力
var locationManager = CLLocationManager() // 追記
override func viewDidLoad() {
requestLoacion() // 追記
mapView.isMyLocationEnabled = true // 追記
}
// 以下、追記
private func requestLoacion() {
// ユーザにアプリ使用中のみ位置情報取得の許可を求めるダイアログを表示
locationManager.requestWhenInUseAuthorization()
// 常に取得したい場合はこちら↓
// locationManager.requestAlwaysAuthorization()
}
シュミレータは位置情報を変更することができるので
今回は徳島中央公園に緯度経度を設定した
緯度:34.0755
経度:134.553
------memo------
日本の緯度経度は
緯度値を +にすると北(上)へ、
経度値を +にすると東(右)へ移動する
参考
https://qiita.com/nwatabou/items/38f4240582d70a4d84a8
現在地に遷移
GoogleMapsの機能で既にあるため、それを利用します。
override func viewDidLoad() {
super.viewDidLoad()
mapView.settings.myLocationButton = true //追加
}
下にタブバーなどがある場合はマップをpaddingすることで解消できる
override func viewDidLoad() {
super.viewDidLoad()
mapView.padding = UIEdgeInsets(top: 55, left: 0, bottom: 49, right: 0) //追加
mapView.settings.myLocationButton = true
}
縮尺によって表示を変更
確認はしてないですが、以下のコードでいけると思います。
var markers:[GMUClusterItem] = [] //追加
func refresh(){
//クラスター全削除
self.clusterManager.clearItems()
for i in 0..<latitudes.count{
let position = CLLocationCoordinate2DMake(latitudes[i], longitudes[i])
let item = POIItem(position: position)
item.text = texts[i]
self.markers.append(item) //追加
self.clusterManager.add(item)
}
}
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
let mapView = self.view as! GMSMapView
// 配列を取り出す
for(marker) in markers{
//GoogleMapsの拡大縮小値を判断
if 9 <= mapView.camera.zoom{
//GoogleMapsの画面表示領域内に含まれるか
if(mapView.projection.contains(marker.position)) {
// 含まれる場合
} else {
// 含まれない場合
}
}else{
self.mapView.clear()
}
}
}
GoogleMapsの種類を変更
4種類のマップが存在する
Swift | 地図のタイプ |
---|---|
normal | 通常の地図 |
satellite | 航空写真 |
hybrid | 通常の地図+航空写真のハイブリッド |
terrain | 地形や樹木などの地形的特徴を持つ地図 |
記述方法は以下の通りです。
mapView.mapType = .satellite
ストリートビューを実装
更新予定
ナイトモード実装
更新予定
フィチャータイプ
更新予定
パディング
更新予定
ポリゴン
更新予定
Jsonファイルの読み取り
SwiftyJsonを使用します
step.1 swiftyJson追加
podfileに
pod "SwiftyJSON"
を追加
------memo------
Jsonファイルとは
メモ(備忘録)
//画面変化時毎回呼ばれる
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
guard let marker = marker else{
return
}
}
//吹き出しをmarkerの上部に表示
func infoViewCenter(_position:CLLocationCoordinate2D){
}
// //吹き出しを消した時呼ばれる
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
}
更新予定