17
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【SwiftUI】MapKitを導入して地図を表示する

Last updated at Posted at 2019-09-18

この投稿は何?

SwiftUIフレークワークは宣言的にコードを記述することで、すばやくアプリケーションを開発できます。ただ、これまでのUIKitやAppKitで利用してきたUIコンポーネントのすべてがSwiftUIフレームワークに含まれてはいません。
この投稿では、SwiftUIフレームワークの中でMapKitビューを利用する 方法を例に、UIKitビューをSwiftUIで扱う手順を解説します。

環境

  • macOS10.15 beta8
  • Xcode11 GM Seed2

この投稿で構築するビューの画面

スクリーンショット 2019-09-19 0.05.25.png

実装手順

Content.swift に直接、地図ビューを構築することもできます。
ただ、_SwiftUIフレームワーク_による開発は 小さいビューを組み合わせる ことをコンセプトとしています。
地図を表示するだけの_カスタムSwiftUIビュー_を作ってから、_Content.swift_で利用することにします。

SUMapView.swift

カスタムSwiftUIビューを記述するファイルです。
MapViewをSwiftUIビューとして利用するので、SUMapViewという名前にしました。

全体のソースコード

プレビュープロバイダは一切、コードを編集していないのでプレビュー定義の部分は省略しています。

SUMapView.swift
import SwiftUI
import MapKit

struct SUMapView: UIViewRepresentable {
    typealias UIViewType = MKMapView
    
    func makeUIView(context: UIViewRepresentableContext<SUMapView>) -> MKMapView {
        MKMapView(frame: .zero)
    }
    
    func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<SUMapView>) {
        let coordinate = CLLocationCoordinate2D(latitude: 37.3351, longitude: -122.0088)
        let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        uiView.setRegion(region, animated: true)
    }
}

SUMapViewの画面

上記コードのプレビューです。
スクリーンショット 2019-09-19 0.10.47.png

1. ファイル作成

新しいファイルを作成します。

  1. メニューバーから「File > New > File...」を選択する。
  2. iOSのSwiftUI Viewを選択して、Nextをクリックする。
  3. ファイル名をSUMapView.swiftをして、Createをクリックする。

2. MapKitフレームワーク

import キーワードで、このファイルにMapKitを導入します。

SUMapView.swiftにMapKitを導入する
import SwiftUI
import MapKit

3. UIViewRepresentableプロトコル

SUMapView構造体にUIRepresentableプロトコルを採用します。

SUMapView
strict SUMapView: UIViewRepresentable {
    ...
}

この時点で、UIViewRepresentableプロトコルに準拠するための要件を満たしていないので、エラーになります。また、SwiftUIビューとしてUIKit系のビューを描画するには、以下に挙げる2点のメソッドを実装する必要があります。

  • makeUIView(context:) -> Self.UIViewType メソッドを実装する
  • updateUIView(_:context:) メソッドを実装する

4. makeUIView(context:) メソッド

makeUIView(context:)メソッドは「SUMapView 構造体がどんなビューか?」を決定します。このメソッドが返すSelf.UIViewType型が、カスタムSwiftUIビューとして扱いたいUIKitビューの型になります。
ここではMKMapView型のことです。

makeUIView(context:)メソッドを記述しようとすると、Xcodeの補完機能が作動します。
該当するメソッドを選択すると、以下のように記述が補完されます。

func makeUIView(context: UIViewRepresentableContext<SUMapView>) -> SUMapView.UIViewType { 
    // code...       
}

// code... の部分に、以下のように記述して MKMapView 型のビューを返すようにします。

makeUIViewメソッドの実装
MKMapView(frame: .zero)    // return は省略

メソッドがUIViewTypeと一致する型を返していないので、下記のエラーが表示されます。
Reference to invalid associated type 'UIViewType' of type 'SUMapView'

5. UIViewType

UIViewType型は未定義なので、任意の型として定義します。

typealias UIViewType = MKMapView

これで、makeUIView(context:)メソッドが宣言する返り値型が MKMapKit(frame: .zero) と一致するようになったので、エラーが解消されます。
ちなみに...
先に UIViewType のタイプエイリアスを定義してから、makeUIView(context:)メソッドを実装すると以下のように補完されます。

func makeUIView(context: UIViewRepresentableContext<SUMapView>) -> MKMapView {
    // code....    
}

返り値の型に注目してください。
タイプエイリアスの宣言を探す必要がなく、ビューの型が理解できます。

6. updateUIView(_:context:) メソッド

このメソッドでは、「ビューが描画されるときの内容」を定義します。ここでは、Appleデベロッパーの聖地 Apple Park の地図を表示することにします。
実装すると、エラーがすべて解消されます。

ContentView.swift

プロジェクトに最初から含まれる既定ファイルです。
このビューに、作成したカスタムSwiftUIビュー SUMapView を組み込むカタチにします。

全体のソースコード

プレビューの定義は省略しました。
デフォルトのTextビューをSUMapViewに置き換えます。

ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        SUMapView()
            .edgesIgnoringSafeArea(.all)
            .statusBar(hidden: true)
    }
}

モディファイアも使えるので、、MapKitビューがSwiftUIビューっぽく利用できていることがわかります。

完成図

スクリーンショット 2019-09-19 0.05.25.png

17
17
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
17
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?