iOS 11 の MapKit の新機能
iOS 11 では、MapKit に次の新機能が追加されます。
ズーム中にマーカーを自動的にグループ化する
サンプルの MapKit サンプル "Tandm" は、新しい iOS 11 注釈クラスタリング機能を実装する方法を示しています。
1. サブクラスを作成するMKPointAnnotation
ポイント注釈クラスは、マップ上の各マーカーを表します。 を使用して、または を使用して MapView.AddAnnotation()
配列 MapView.AddAnnotations()
から個別に追加できます。
ポイント注釈クラスには視覚的表現はありません。マーカーに関連付けられているデータ (最も重要なのは、マップ上の緯度と経度であるプロパティ)、 Coordinate
およびカスタム プロパティを表すためにのみ必要です。
public class Bike : MKPointAnnotation
{
public BikeType Type { get; set; } = BikeType.Tricycle;
public Bike(){}
public Bike(NSNumber lat, NSNumber lgn, NSNumber type)
{
Coordinate = new CLLocationCoordinate2D(lat.NFloatValue, lgn.NFloatValue);
switch(type.NUIntValue) {
case 0:
Type = BikeType.Unicycle;
break;
case 1:
Type = BikeType.Tricycle;
break;
}
}
}
2. 単一マーカーのサブクラスを作成するMKMarkerAnnotationView
マーカー注釈ビューは、各注釈の視覚的表現であり、次のようなプロパティを使用してスタイルが設定されます。
- MarkerTintColor – マーカーの色。
- GlyphText – マーカーに表示されるテキスト。
- GlyphImage – マーカーに表示されるイメージを設定します。
- DisplayPriority – マップがマーカーで混雑している場合の z オーダー (積み重ね動作) を決定します。 、
DefaultHigh
、または のいずれかをRequired
使用しますDefaultLow
。
自動クラスタリングをサポートするには、次も設定する必要があります。
- ClusteringIdentifier – クラスター化されるマーカーを制御します。 すべてのマーカーに同じ識別子を使用することも、異なる識別子を使用してグループ化方法を制御することもできます。
[Register("BikeView")]
public class BikeView : MKMarkerAnnotationView
{
public static UIColor UnicycleColor = UIColor.FromRGB(254, 122, 36);
public static UIColor TricycleColor = UIColor.FromRGB(153, 180, 44);
public override IMKAnnotation Annotation
{
get {
return base.Annotation;
}
set {
base.Annotation = value;
var bike = value as Bike;
if (bike != null){
ClusteringIdentifier = "bike";
switch(bike.Type){
case BikeType.Unicycle:
MarkerTintColor = UnicycleColor;
GlyphImage = UIImage.FromBundle("Unicycle");
DisplayPriority = MKFeatureDisplayPriority.DefaultLow;
break;
case BikeType.Tricycle:
MarkerTintColor = TricycleColor;
GlyphImage = UIImage.FromBundle("Tricycle");
DisplayPriority = MKFeatureDisplayPriority.DefaultHigh;
break;
}
}
}
}
3. マーカーのクラスターを表す を MKAnnotationView
作成する
マーカーのクラスターを表す注釈ビューは単純な画像である 可能性 があります。一方、ユーザーは、複数のマーカーがグループ化されたマーカーの数に関する視覚的な手掛かりをアプリに提供することを期待しています。
このサンプル コードでは、CoreGraphics を使用してクラスター内のマーカーの数と、各マーカーの種類の割合の円グラフ表現をレンダリングします。
次の設定も行う必要があります。
- DisplayPriority – マップがマーカーで混雑している場合の z オーダー (積み重ね動作) を決定します。 、
DefaultHigh
、または のいずれかをRequired
使用しますDefaultLow
。 - CollisionMode –
Circle
またはRectangle
。
[Register("ClusterView")]
public class ClusterView : MKAnnotationView
{
public static UIColor ClusterColor = UIColor.FromRGB(202, 150, 38);
public override IMKAnnotation Annotation
{
get {
return base.Annotation;
}
set {
base.Annotation = value;
var cluster = MKAnnotationWrapperExtensions.UnwrapClusterAnnotation(value);
if (cluster != null)
{
var renderer = new UIGraphicsImageRenderer(new CGSize(40, 40));
var count = cluster.MemberAnnotations.Length;
var unicycleCount = CountBikeType(cluster.MemberAnnotations, BikeType.Unicycle);
Image = renderer.CreateImage((context) => {
// Fill full circle with tricycle color
BikeView.TricycleColor.SetFill();
UIBezierPath.FromOval(new CGRect(0, 0, 40, 40)).Fill();
// Fill pie with unicycle color
BikeView.UnicycleColor.SetFill();
var piePath = new UIBezierPath();
piePath.AddArc(new CGPoint(20,20), 20, 0, (nfloat)(Math.PI * 2.0 * unicycleCount / count), true);
piePath.AddLineTo(new CGPoint(20, 20));
piePath.ClosePath();
piePath.Fill();
// Fill inner circle with white color
UIColor.White.SetFill();
UIBezierPath.FromOval(new CGRect(8, 8, 24, 24)).Fill();
// Finally draw count text vertically and horizontally centered
var attributes = new UIStringAttributes() {
ForegroundColor = UIColor.Black,
Font = UIFont.BoldSystemFontOfSize(20)
};
var text = new NSString($"{count}");
var size = text.GetSizeUsingAttributes(attributes);
var rect = new CGRect(20 - size.Width / 2, 20 - size.Height / 2, size.Width, size.Height);
text.DrawString(rect, attributes);
});
}
}
}
public ClusterView(){}
public ClusterView(MKAnnotation annotation, string reuseIdentifier) : base(annotation, reuseIdentifier)
{
DisplayPriority = MKFeatureDisplayPriority.DefaultHigh;
CollisionMode = MKAnnotationViewCollisionMode.Circle;
// Offset center point to animate better with marker annotations
CenterOffset = new CoreGraphics.CGPoint(0, -10);
}
private nuint CountBikeType(IMKAnnotation[] members, BikeType type) {
nuint count = 0;
foreach(Bike member in members){
if (member.Type == type) ++count;
}
return count;
}
}
4. ビュー クラスを登録する
マップ ビュー コントロールを作成してビューに追加する場合は、注釈ビューの種類を登録して、マップの拡大と縮小時のクラスタリングの自動動作を有効にします。
MapView.Register(typeof(BikeView), MKMapViewDefault.AnnotationViewReuseIdentifier);
MapView.Register(typeof(ClusterView), MKMapViewDefault.ClusterAnnotationViewReuseIdentifier);
5. マップをレンダリングします。
マップをレンダリングすると、ズーム レベルに応じて注釈マーカーがクラスター化またはレンダリングされます。 ズーム レベルが変化すると、マーカーはクラスター内およびクラスター外でアニメーション化されます。
MapKit を使用したデータの表示の詳細については、「Maps」セクションを参照してください。
コンパス ボタン
iOS 11 では、コンパスをマップからポップしてビュー内の他の場所にレンダリングする機能が追加されています。 例については、 Tandm サンプル アプリ を参照してください。
コンパスのように見えるボタン (マップの向きが変更されたときのライブ アニメーションを含む) を作成し、別のコントロールにレンダリングします。
次のコードでは、コンパス ボタンを作成し、ナビゲーション バーにレンダリングします。
var compass = MKCompassButton.FromMapView(MapView);
compass.CompassVisibility = MKFeatureVisibility.Visible;
NavigationItem.RightBarButtonItem = new UIBarButtonItem(compass);
MapView.ShowsCompass = false; // so we don't have two compasses!
プロパティを ShowsCompass
使用して、マップ ビュー内の既定のコンパスの表示を制御できます。
スケール ビュー
メソッドを使用して MKScaleView.FromMapView()
ビューの他の場所にスケールを追加し、ビュー階層内の他の場所に追加するスケール ビューのインスタンスを取得します。
var scale = MKScaleView.FromMapView(MapView);
scale.LegendAlignment = MKScaleViewAlignment.Trailing;
scale.TranslatesAutoresizingMaskIntoConstraints = false;
View.AddSubview(scale); // constraints omitted for simplicity
MapView.ShowsScale = false; // so we don't have two scale displays!
プロパティを ShowsScale
使用して、マップ ビュー内の既定のコンパスの表示を制御できます。
[ユーザー追跡] ボタン
ユーザー追跡ボタンは、ユーザーの現在の場所にマップを中央に配置します。 ボタンのインスタンスを MKUserTrackingButton.FromMapView()
取得し、書式設定の変更を適用し、ビュー階層内の他の場所に追加するには、 メソッドを使用します。
var button = MKUserTrackingButton.FromMapView(MapView);
button.Layer.BackgroundColor = UIColor.FromRGBA(255,255,255,80).CGColor;
button.Layer.BorderColor = UIColor.White.CGColor;
button.Layer.BorderWidth = 1;
button.Layer.CornerRadius = 5;
button.TranslatesAutoresizingMaskIntoConstraints = false;
View.AddSubview(button); // constraints omitted for simplicity