Новые возможности MapKit в iOS 11

IOS 11 добавляет следующие новые функции в MapKit:

Карта с кластеризованными маркерами и кнопкой компаса

Автоматическое группирование маркеров при масштабировании

В примере показано, как реализовать новую функцию заметки 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-порядок (поведение стека) при переполнении карты маркерами. Используйте один из Required, DefaultHighили 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-порядок (поведение стека) при переполнении карты маркерами. Используйте один из Required, DefaultHighили DefaultLow.
  • CollisionModeCircle или 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 см. в разделе Карты.

Кнопка компаса

iOS 11 добавляет возможность всплывать компас из карты и отображать его в другом месте в представлении.

Создайте кнопку, которая выглядит как компас (включая динамическую анимацию при изменении ориентации карты) и отрисовывает ее на другом элементе управления.

Кнопка компаса в панели навигации

Приведенный ниже код создает кнопку компаса и отрисовывает ее на панели навигации:

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