Compartir a través de


Nuevas características de MapKit en iOS 11

iOS 11 agrega las siguientes características nuevas a MapKit:

Mapa que muestra los marcadores agrupados y el botón de brújula

Agrupación automática de marcadores al hacer zoom

En el ejemplo se muestra cómo implementar la nueva característica de agrupación en clústeres de anotaciones de iOS 11.

1. Creación de una subclase MKPointAnnotation

La clase de anotación de punto representa cada marcador del mapa. Se pueden agregar individualmente mediante MapView.AddAnnotation() o desde una matriz mediante MapView.AddAnnotations().

Las clases de anotación de punto no tienen una representación visual, solo son necesarias para representar los datos asociados al marcador (lo más importante, la propiedad Coordinate, que es su latitud y longitud en el mapa) y las propiedades personalizadas:

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. Creación de una subclase MKMarkerAnnotationView para marcadores únicos

La vista de anotación de marcador es la representación visual de cada anotación y se le aplica estilo mediante propiedades como:

  • MarkerTintColor: color del marcador.
  • GlyphText: texto mostrado en el marcador.
  • GlyphImage: establece la imagen que se muestra en el marcador.
  • DisplayPriority: determina el orden z (comportamiento de apilamiento) cuando el mapa está lleno de marcadores. Use Required, DefaultHigh o DefaultLow.

Para admitir la agrupación automática en clústeres, también debe establecer:

  • ClusteringIdentifier: controla qué marcadores se agrupan en clústeres. Puede usar el mismo identificador para todos los marcadores o usar identificadores diferentes para controlar la forma en que se agrupan.
[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. Creación de una MKAnnotationView para representar clústeres de marcadores

Aunque la vista de anotación que representa un clúster de marcadores podría ser una imagen sencilla, los usuarios esperan que la aplicación proporcione indicaciones visuales sobre cuántos marcadores se han agrupado.

El ejemplo usa CoreGraphics para representar el número de marcadores del clúster, así como una representación de gráfico en círculos de la proporción de cada tipo de marcador.

También debe establecer:

  • DisplayPriority: determina el orden z (comportamiento de apilamiento) cuando el mapa está lleno de marcadores. Use Required, DefaultHigh o DefaultLow.
  • CollisionMode: Circle o 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. Registro de las clases de vista

Cuando se cree el control de vista de mapa y se agrega a una vista, registre los tipos de vista de anotación para habilitar el comportamiento automático de agrupación en clústeres a medida que se acerque y se aleje el mapa:

MapView.Register(typeof(BikeView), MKMapViewDefault.AnnotationViewReuseIdentifier);
MapView.Register(typeof(ClusterView), MKMapViewDefault.ClusterAnnotationViewReuseIdentifier);

5. Representación del mapa

Cuando se representa el mapa, los marcadores de anotación se agruparán o representarán en función del nivel de zoom. A medida que cambia el nivel de zoom, los marcadores se animan entrando y saliendo de los clústeres.

Simulador que muestra marcadores agrupados en el mapa

Consulte la sección Mapas para obtener más información sobre cómo mostrar datos con MapKit.

Botón de brújula

iOS 11 agrega la capacidad de sacar la brújula del mapa y representarla en otro lugar de la vista.

Cree un botón que parezca una brújula (incluida la animación en directo cuando se cambia la orientación del mapa) y represéntela en otro control.

Botón Brújula en la barra de navegación

El código siguiente crea un botón de brújula y lo representa en la barra de navegación:

var compass = MKCompassButton.FromMapView(MapView);
compass.CompassVisibility = MKFeatureVisibility.Visible;
NavigationItem.RightBarButtonItem = new UIBarButtonItem(compass);
MapView.ShowsCompass = false; // so we don't have two compasses!

La propiedad ShowsCompass se puede usar para controlar la visibilidad de la brújula predeterminada dentro de la vista de mapa.

Vista de escala

Agregue la escala en otra parte de la vista mediante el método MKScaleView.FromMapView() para obtener una instancia de la vista de escala para agregarla en otra parte de la jerarquía de vistas.

Vista de escalado superpuesta en un mapa

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!

La propiedad ShowsScale se puede usar para controlar la visibilidad de la brújula predeterminada dentro de la vista de mapa.

Botón de seguimiento del usuario

El botón de seguimiento del usuario centra el mapa en la ubicación actual del usuario. Use el método MKUserTrackingButton.FromMapView() para obtener una instancia del botón, aplicarle cambios de formato y agregarlo a otra parte de la jerarquía de vistas.

Botón ubicación de usuario superpuesto en un mapa

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