LoginSignup
6
6

More than 3 years have passed since last update.

[iOSアプリ開発]GoogleMaps SDK for iOS 逆引き辞書

Last updated at Posted at 2020-05-07

はじめに

アプリ開発を行った際、GoogleMapsを使用しましたが記事が少なく時間がかかった為備忘録として記録

目次

  • インストール方法
    • GoogleMapsを表示
  • マーカーを表示(クラスター化)
    • クラスタの画像を変更
    • クラスタタップ時の処理
    • マーカーの画像を変更
    • マーカーの吹き出しを表示
    • マーカーを消去
  • マーカーを表示(単体表示)
  • 現在地をマップに表示
    • 現在地に遷移
  • 縮尺度によって処理を行う
  • GoogleMapsの地図の種類を変更

以下更新予定

  • ストリートビューを実装
  • ナイトモード実装
  • フィチャータイプ
  • パディング
  • ポリゴン
  • Jsonファイルの読み取り

環境

日付 : 2020年5月
MacOS : Catalina
Xcode Version : 11.41
cocoapods Version : 1.9.0.beta.3

インストール方法

step.1 新規プロジェクトを作成

プロジェクト名をGoogleMapsTest とした

step.2 ココアポッドをインストール(インストール済ならしなくてよい)

ターミナルに以下のコマンドを入力

Akidon.terminal
sudo gem install cocoapods
Akidon.terminal
pod setup

------memo------

Akidon.terminal
pod --version

でバージョンの確認ができる

step.4 Podfile作成

ターミナルを開きファイルまでのパスを通す

Akidon.terminal
cd <ファイルのパス>

podfileを作成

Akidon.terminal
pod init

podfileが作成されたので、
以下2つのコードを追加

GoogleMapsTest.podfile
pod 'GoogleMaps'
pod 'GooglePlaces'

スクリーンショット 2020-05-05 18.53.35.png

入力後以下のコードをターミナルで入力

Akidon.terminal
pod install

一度Xcodeを終了させる。
finderから<プロジェクト名>.xcworkspaceを開く

こうなればOK
スクリーンショット 2020-05-05 18.07.24.png

------memo------
これで簡単にパスを通すことができる
スクリーンショット 2020-05-05 17.49.25(2).png

cd <ファイルのパス> の<>は入力しなくて良い

GoogleMapsを表示

step.1 APIの設定

ここからGet Startedを選択

メニューバー(ハンバーガーメニュー)からAPIとサービス -> ライブラリ -> Maps SDK for iOSを選択
APIを有効にする
ここ書いてる記事少なくてつまづいた

次に
APIとサービス -> ライブラリ -> 認証情報から
認証情報を作成をタップするとAPIキーが作成され、キー制限をタップする

アプリケーションの制限からiOSアプリを選択
プロジェクトのバンドルIDを入力する。

------memo------
バンドルIDの見方
スクリーンショット 2020-05-05 18.24.00.png

step.2 マップ表示

XcodeのAppDelegate.swiftに以下のコードを追加

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で以下のコードを入力

ViewController.swift
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
    }
}

起動すると以下の画面になります。
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-05 at 18.51.27.png

------memo------
シュミレーターの言語設定を日本語にすることで、GoogleMapsを日本語表示にできる。

参考
https://blog.codecamp.jp/iphone-app-develope-original-googlemap

マーカーを表示(クラスター化)

podfileに以下を追加しpod installする

GoogleMapsTest.podfile
pod 'Google-Maps-iOS-Utils'

ViewControllerに以下のコードを記述
markerが4つ以上からクラスター化になる

ViewController.swift
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
  }
}

Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-06 at 12.17.31.png

クラスタの画像を変更

ViewController.swift
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する
スクリーンショット 2020-05-06 12.03.43.png
そして、Assets内にドラッグアンドドロップする事で画像でまとめることができる
スクリーンショット 2020-05-06 12.32.49.png

クラスタがタップされた時

今回はクラスタがタップされた時、ズームするようにした。

ViewController.swift
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
    }
}

マーカーの画像を変更

ViewController.swift
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")
    }
}

Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-06 at 12.45.06.png

マーカーの吹き出しを表示する

新しいViewControllerとxibファイルを作成

名前は
InfoViewController.swift
InfoView.xib
とした。

xibファイルのサイズはwidth:300 height:200にした
スクリーンショット 2020-05-06 17.57.53.png
吹き出し画面はこのような感じにします。

InfoViewController.swift

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のコード全文です。
見辛くてすみません

ViewController.swift

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より
スクリーンショット 2020-05-06 17.51.35.png

スクリーンショット 2020-05-06 17.51.58.png

ViewControllerならCocoaTouchClass,xibファイルはViewを選択する

xibファイルのサイズ変更はFreeformにすることで変更可能

スクリーンショット 2020-05-06 17.45.17.png
スクリーンショット 2020-05-06 17.44.48.png

guardを使うことでオプショナル記号(!,?)を無くし美しいコードになる
またエラーが発見しやすい場合もしばしば。。。

参考
https://qiita.com/akidon0000/items/bf67f052179c1f704699

マーカーを消去

上のコードで記述済なのでそちらを見てちょ

ViewController.swift
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 を使用します

スクリーンショット 2020-05-06 21.19.25.png

スクリーンショット 2020-05-06 21.23.46.png

次にViewControllerに以下のコードを入力

ViewController.swift
    var locationManager = CLLocationManager() // 追記

    override func viewDidLoad() {
        requestLoacion() // 追記
        mapView.isMyLocationEnabled = true // 追記
    }

    // 以下、追記
    private func requestLoacion() {
        // ユーザにアプリ使用中のみ位置情報取得の許可を求めるダイアログを表示
        locationManager.requestWhenInUseAuthorization()
        // 常に取得したい場合はこちら↓
        // locationManager.requestAlwaysAuthorization()
    }

ビルドしてみると
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-06 at 21.43.11.png
通知が来るので承諾

シュミレータは位置情報を変更することができるので
今回は徳島中央公園に緯度経度を設定した
緯度:34.0755
経度:134.553
スクリーンショット 2020-05-06 21.52.34.png

------memo------
日本の緯度経度は
緯度値を +にすると北(上)へ、
経度値を +にすると東(右)へ移動する

参考
https://qiita.com/nwatabou/items/38f4240582d70a4d84a8

現在地に遷移

GoogleMapsの機能で既にあるため、それを利用します。

ViewController.swift
  override func viewDidLoad() {
     super.viewDidLoad()
     mapView.settings.myLocationButton = true //追加
  }

Simulator Screen Shot - iPhone SE (2nd generation) - 2020-05-06 at 22.05.57.png

下にタブバーなどがある場合はマップをpaddingすることで解消できる

ViewController.swift
  override func viewDidLoad() {
     super.viewDidLoad()
     mapView.padding = UIEdgeInsets(top: 55, left: 0, bottom: 49, right: 0) //追加
     mapView.settings.myLocationButton = true 
  }

縮尺によって表示を変更

確認はしてないですが、以下のコードでいけると思います。

ViewController.swift
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 地形や樹木などの地形的特徴を持つ地図

記述方法は以下の通りです。

ViewController.swift
mapView.mapType = .satellite

ストリートビューを実装

更新予定

ナイトモード実装

更新予定

フィチャータイプ

更新予定

パディング

更新予定

ポリゴン

更新予定

Jsonファイルの読み取り

SwiftyJsonを使用します

step.1 swiftyJson追加

podfileに

GoogleMapsTest.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) {
    }

更新予定

参考
https://blog.codecamp.jp/programming-swift-json

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6