はじめに
WWDC19で発表された内容が濃すぎて,
調べて使ってみたいけど扱えていない内容がまだまだたくさんです😅
今回はそのひとつで iOS 13 から新しく MapKit に追加され
Point of Interest(POI) フィルタリングを触ってみました。
このセッションの短い時間ではありましたが紹介されています。(10分あたりから)
What’s New in MapKit and MapKit JS
https://developer.apple.com/videos/play/wwdc2019/236/
MapKit における POI とは
MapKit における POI とは Map に表示されている,
レストランとか学校など様々な場所表示です。それぞれアイコンもあります。
言われてみればあーあれかってなりますよね。
種類的には,執筆時点で 40種類あります。(セッション資料より)
Apple Park みたいな特別な場所は専用のものが用意されているようです。
MapKit チームはデベロッパにもっとこういうのあったらどうか?と
ユースケースや提案があれば是非頼むというスタンスのようです。
参考:MKPointOfInterestCategory
https://developer.apple.com/documentation/mapkit/mkpointofinterestcategory?language=swift
iOS 12 までの POI のフィルタリング
通常の Map アプリでは情報が多くてもいいと思います。
都市部だと明らかにアイコンの乱立がありユーザの目移りが問題になります。
それぞれのアプリの Map の用途によって
POI のフィルタリング(出し分け)ができるとアプリの UX が上がりそうです。
iOS 12 までは,POI の全表示 or 全非表示はできていました。
MapView の showsPointsOfInterest
に Bool 値を与えていました。
コードは下記です。
let mapView = MKMapView()
mapView.showsPointsOfInterest = false
こんな感じで POI が非表示になります。全部非表示にはならないんだなぁ。
純正 Map 自体も更新されているだろうし,APIの方がついていってない感じ。
デフォルト | 設定OFF |
---|---|
参考:showsPointsOfInterest
https://developer.apple.com/documentation/mapkit/mkmapview/1452102-showspointsofinterest?language=swift
iOS 13 以降の POI フィルタリング
今までは,POI を全表示 or 全非表示しかできなかったですが,
iOS 13 からフィルタリングが可能になりました。
showsPointsOfInterest
は
iOS 13 以降では,レファレンスの通り Deprecated になっています。
代わりに pointOfInterestFilter
を使うように変わっています。
参考:pointOfInterestFilter
https://developer.apple.com/documentation/mapkit/mkmapview/3143417-pointofinterestfilter?language=swift
pointOfInterestFilter
を使って,POI のフィルタリングができます。
前述した MKPointOfInterestCategory
が出し分けとして利用されます。
以下 3種類の POI フィルタリングを紹介します。
都合によりスクショを貼っていますが,
マップの拡大・縮小を行なっても設定は維持されます。
この POI だけ表示したい,ケース
POI が多いので レストランやカフェ,フードマーケットだけ表示させたいなぁ〜
そういう場合は,MKPointOfInterestCategory
を格納する Array
を用意して,
レストランとカフェ,フードマーケットをフィルタリングします。
include
なので直感的でわかりやすいですね。
let category: [MKPointOfInterestCategory] = [.restaurant, .cafe, .foodMarket]
let filter = MKPointOfInterestFilter(including: category)
mapView.pointOfInterestFilter = filter
実行結果はこちらです。まだ多い気もするけどだいぶ絞れましたね。
よく見るとデフォルトだと表示されないレストランも表示されています。
これでレストラン探しが捗りそうな気がしますね!
デフォルト | フィルタリング後 |
---|---|
この POI だけ表示したくない,ケース
逆に飲食店を非表示にしたいなぁ〜 みたいなケースがあったとしましょう。
逆に exclude
で該当のカテゴリのみ非表示にできます。
let category: [MKPointOfInterestCategory] = [.restaurant, .foodMarket, .cafe]
let filter = MKPointOfInterestFilter(excluding: category)
mapView.pointOfInterestFilter = filter
こちらも飲食店が非表示になって表示されるようになったお店などがあります。
デフォルト | フィルタリング後 |
---|---|
全部表示しない,ケース
該当のアノテーション以外の情報はいらないんや〜
という場合は excludingAll
を使います。
let filter = MKPointOfInterestFilter.excludingAll
mapView.pointOfInterestFilter = filter
結果は下記です(左側)。
今までの showsPointsOfInterest
が false
のときよりも POI が消えてますね。
あまりないケースとは思います。
[新]全て非表示 | [旧]全て非表示 |
---|---|
具体的なユースケース(妄想)🤔
POIのフィルタリングはむやみに設定するものではなく,
アプリを利用するユーザのことを意識して行うべきです。
何個か妄想してみました。
例1
例えば,飲食店のアプリでお店の場所を示すのに住所と Map を掲載するとします。
お店に駐車場がないので周りのコインパーキングなどを利用してほしいとします。
この場合,駐車場のアイコンだけを表示するようにすると
ユーザは他のロケーションに目移りせずに駐車場の位置を意識できます。
let category: [MKPointOfInterestCategory] = [.parking]]
let filter = MKPointOfInterestFilter(including: category)
mapView.pointOfInterestFilter = filter
フィルタリング前 | フィルタリング後 |
---|---|
例2
つぎは海外カンファレンスなどのイベント系アプリを考えてみます。
同じく会場の場所を示すのに住所と Map を掲載するとします。
世界各国のデベロッパが会場のある街(都市,国)に訪れます。
ユーザが気になるのは会場近くのレストラン,ホテル,駅なのではないでしょうか?
あるいは観光する人もいるかもしれません。
let category: [MKPointOfInterestCategory]
= [.hotel,
.restaurant, .cafe,
.airport, .publicTransport,
.beach, .amusementPark, .nightlife]
let filter = MKPointOfInterestFilter(including: category)
mapView.pointOfInterestFilter = filter
フィルタリング前 | フィルタリング後 |
---|---|
うーんイマイチかな。マップ拡大しないと恩恵少ない。
WWDC は会場から近いホテルがおすすめです。(私はフェアモント派)
VTA ライトレール使えるので駅近もいいですね。
現地では,体調第一でいろいろ決めると良いです。時差もありますしね。
来年も行きたいなぁ🤩
SwiftUI でも使える
SwiftUI でも MapKit を使っているので,書き方はほぼ同じです。
SwiftUI での MapView の使い方の復習と
POIフィルタリングを記事にしようと思っていたのですが計画狂っちゃいました。
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView {
VStack(alignment: .leading) {
// MapView Part
MapView()
.frame(height: 300.0)
// 省略
}
}
}
}
UIViewRepresentable に準拠させます。
makeUIView
関数内で frame のコード書いても無視されるっぽくて
初期化する View で設定するとちゃんと意図通りに動くようです。
import SwiftUI
import MapKit // これが必須
struct MapView: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView(frame: .zero)
return mapView
}
// Required
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {
let center = CLLocationCoordinate2DMake(緯度, 経度)
let span = MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002)
let region = MKCoordinateRegion(center: center, span: span)
uiView.setRegion(region, animated: true)
// POIフィルタリング(UIKitと使い方は変わらない)
let category: [MKPointOfInterestCategory] = [.parking, .publicTransport]
let filter = MKPointOfInterestFilter(including: category)
uiView.pointOfInterestFilter = filter
// アノテーションを表示
let annotation = MKPointAnnotation()
annotation.coordinate = center
annotation.title = "アノテーションタイトル"
uiView.addAnnotation(annotation)
}
}
Enjoy SwiftUI vol2 でライブコーディングした
サンプルアプリのイベント詳細画面を更新してみました。
フィルタリングは [.parking, .publicTransport]
にしています。
駐車場や駅の情報が確認できますね。
サンプルコードはこちらです。
https://github.com/MilanistaDev/StudyGroupEventFetcherForSwiftUI
connpass の API 叩いてリストに表示する部分の記事もよろしければご覧ください。
【SwiftUI】外部APIを叩いて取得した結果をListに表示する
https://qiita.com/MilanistaDev/items/64dca8c9d5099a19529e
おわりに
今回は,iOS 13 の Mapkit に新しく追加された
POI のフィルタリングについて書きました。
頭の片隅に置いて,これからは Map を実装する際は,
Map 上の POI のことを少し意識してみてはいかがでしょうか?
ユーザの目線で考えることが大事です。
ご覧いただきありがとうございました!
今年も多くの方に支えられて楽しく1年過ごすことができました。
来年も引き続きアウトプット駆動で精進していこうと思っておリます!