Mapas em Xamarin.iOS
Os mapas são uma característica comum em todos os sistemas operacionais móveis modernos. O iOS oferece suporte a mapeamento nativamente por meio da estrutura do Map Kit. Com o Map Kit, os aplicativos podem facilmente adicionar mapas ricos e interativos. Esses mapas podem ser personalizados de várias maneiras, como adicionar anotações para marcar locais em um mapa e sobrepor gráficos de formas arbitrárias. O Map Kit ainda tem suporte integrado para mostrar a localização atual de um dispositivo.
Adicionando um mapa
A adição de um mapa a um aplicativo é realizada adicionando uma MKMapView
instância à hierarquia de exibição, conforme mostrado abaixo:
// map is an MKMapView declared as a class variable
map = new MKMapView (UIScreen.MainScreen.Bounds);
View = map;
MKMapView
é uma UIView
subclasse que exibe um mapa. Basta adicionar o mapa usando o código acima para obter um mapa interativo:
Estilo do mapa
MKMapView
suporta 3 estilos diferentes de mapas. Para aplicar um estilo de mapa, basta definir a MapType
propriedade como um valor da MKMapType
enumeração:
map.MapType = MKMapType.Standard; //road map
map.MapType = MKMapType.Satellite;
map.MapType = MKMapType.Hybrid;
A captura de tela a seguir mostra os diferentes estilos de mapa disponíveis:
Movimento panorâmico e zoom
MKMapView
Inclui suporte para recursos de interatividade de mapas, como:
- Zoom através de um gesto de pinça
- Movimento panorâmico através de um gesto panorâmico
Esses recursos podem ser habilitados ou desabilitados simplesmente definindo as ZoomEnabled
propriedades e ScrollEnabled
da MKMapView
instância, onde o valor padrão é verdadeiro para ambos. Por exemplo, para exibir um mapa estático, basta definir as propriedades apropriadas como false:
map.ZoomEnabled = false;
map.ScrollEnabled = false;
Local do usuário
Além da interação do usuário, MKMapView
também tem suporte embutido para exibir a localização do dispositivo. Ele faz isso usando a estrutura de localização principal. Antes de acessar o local do usuário, você deve avisá-lo. Para fazer isso, crie uma instância de CLLocationManager
e chame RequestWhenInUseAuthorization
.
CLLocationManager locationManager = new CLLocationManager();
locationManager.RequestWhenInUseAuthorization();
//locationManager.RequestAlwaysAuthorization(); //requests permission for access to location data while running in the background
Observe que em versões do iOS anteriores à 8.0, a tentativa de chamar RequestWhenInUseAuthorization
resultará em um erro. Certifique-se de verificar a versão do iOS antes de fazer essa chamada se você pretende oferecer suporte a versões anteriores ao 8.
O acesso à localização do usuário também requer modificações no Info.plist. As seguintes chaves relacionadas a dados locais devem ser definidas:
- NSLocationWhenInUseUsageDescription – para acessar o local do usuário enquanto eles estiverem interagindo com seu aplicativo.
- NSLocationAlwaysUsageDescription – para quando o aplicativo acessa o local do usuário em segundo plano.
Você pode adicionar essas chaves abrindo Info.plist e selecionando Fonte na parte inferior do editor.
Depois de atualizar o Info.plist e solicitar permissão ao usuário para acessar sua localização, você poderá mostrar a localização do usuário no mapa definindo a ShowsUserLocation
propriedade como true:
map.ShowsUserLocation = true;
Anotações
MKMapView
também suporta a exibição de imagens, conhecidas como anotações, em um mapa. Podem ser imagens personalizadas ou pinos definidos pelo sistema de várias cores. Por exemplo, a captura de tela a seguir mostra um mapa com um pino e uma imagem personalizada:
Adicionando uma anotação
Uma anotação em si tem duas partes:
- O
MKAnnotation
objeto, que inclui dados de modelo sobre a anotação, como o título e o local da anotação. - O
MKAnnotationView
, que contém a imagem a ser exibida e, opcionalmente, um texto explicativo que é mostrado quando o usuário toca na anotação.
O Map Kit usa o padrão de delegação do iOS para adicionar anotações a um mapa, onde a Delegate
propriedade do MKMapView
é definida como uma instância de um MKMapViewDelegate
arquivo . É a implementação desse delegado que é responsável por devolver o MKAnnotationView
para uma anotação.
Para adicionar uma anotação, primeiro a anotação é adicionada chamando AddAnnotations
a MKMapView
instância:
// add an annotation
map.AddAnnotations (new MKPointAnnotation (){
Title="MyAnnotation",
Coordinate = new CLLocationCoordinate2D (42.364260, -71.120824)
});
Quando o local da anotação se torna visível no mapa, o chamará o MKMapView
método de GetViewForAnnotation
seu representante para exibir o MKAnnotationView
para exibir.
Por exemplo, o código a seguir retorna um sistema fornecido MKPinAnnotationView
:
string pId = "PinAnnotation";
public override MKAnnotationView GetViewForAnnotation (MKMapView mapView, NSObject annotation)
{
if (annotation is MKUserLocation)
return null;
// create pin annotation view
MKAnnotationView pinView = (MKPinAnnotationView)mapView.DequeueReusableAnnotation (pId);
if (pinView == null)
pinView = new MKPinAnnotationView (annotation, pId);
((MKPinAnnotationView)pinView).PinColor = MKPinAnnotationColor.Red;
pinView.CanShowCallout = true;
return pinView;
}
Reutilizando anotações
Para conservar memória, MKMapView
permite que as exibições de anotação sejam agrupadas para reutilização, semelhante à maneira como as células da tabela são reutilizadas. A obtenção de uma exibição de anotação do pool é feita com uma chamada para DequeueReusableAnnotation
:
MKAnnotationView pinView = (MKPinAnnotationView)mapView.DequeueReusableAnnotation (pId);
Mostrando textos explicativos
Como mencionado anteriormente, uma anotação pode opcionalmente mostrar um texto explicativo. Para mostrar um texto explicativo, basta definir CanShowCallout
como true no MKAnnotationView
. Isso resulta na exibição do título da anotação quando a anotação é tocada, conforme mostrado:
Personalizando o texto explicativo
O texto explicativo também pode ser personalizado para mostrar as visualizações do acessório esquerdo e direito, conforme mostrado abaixo:
pinView.RightCalloutAccessoryView = UIButton.FromType (UIButtonType.DetailDisclosure);
pinView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile ("monkey.png"));
Esse código resulta no seguinte texto explicativo:
Para manipular o usuário tocando no acessório certo, basta implementar o CalloutAccessoryControlTapped
método no MKMapViewDelegate
:
public override void CalloutAccessoryControlTapped (MKMapView mapView, MKAnnotationView view, UIControl control)
{
...
}
Sobreposições
Outra maneira de colocar gráficos em camadas em um mapa é usando sobreposições. As sobreposições são suporte para elaborar conteúdos gráficos que são dimensionados com o mapa conforme ele é ampliado e reduzido. O iOS fornece suporte para vários tipos de sobreposições, incluindo:
- Polígonos - Comumente usados para destacar alguma região em um mapa.
- Polilinhas - Muitas vezes vistas ao mostrar uma rota.
- Círculos - Usado para realçar uma área circular de um mapa.
Além disso, sobreposições personalizadas podem ser criadas para mostrar geometrias arbitrárias com código de desenho granular e personalizado. Por exemplo, o radar meteorológico seria um bom candidato para uma sobreposição personalizada.
Adicionando uma sobreposição
Semelhante às anotações, adicionar uma sobreposição envolve 2 partes:
- Criando um objeto de modelo para a sobreposição e adicionando-o
MKMapView
ao . - Criando um modo de exibição para a sobreposição no
MKMapViewDelegate
.
O modelo para a sobreposição pode ser qualquer MKShape
subclasse. O Xamarin.iOS inclui MKShape
subclasses para polígonos, polilinhas e círculos, por meio das MKPolygon
classes , MKPolyline
e MKCircle
respectivamente.
Por exemplo, o código a seguir é usado para adicionar um MKCircle
:
var circleOverlay = MKCircle.Circle (mapCenter, 1000);
map.AddOverlay (circleOverlay);
A exibição de uma sobreposição é uma MKOverlayView
instância retornada GetViewForOverlay
pelo no MKMapViewDelegate
. Cada MKShape
um tem um correspondente MKOverlayView
que sabe como exibir a forma dada. Pois MKPolygon
há MKPolygonView
. Da mesma forma, MKPolyline
corresponde a MKPolylineView
, e para MKCircle
lá há MKCircleView
.
Por exemplo, o código a seguir retorna um MKCircleView
para um MKCircle
:
public override MKOverlayView GetViewForOverlay (MKMapView mapView, NSObject overlay)
{
var circleOverlay = overlay as MKCircle;
var circleView = new MKCircleView (circleOverlay);
circleView.FillColor = UIColor.Blue;
return circleView;
}
Isso exibe um círculo no mapa, conforme mostrado:
Pesquisa local
O iOS inclui uma API de pesquisa local com o Map Kit, que permite pesquisas assíncronas para pontos de interesse em uma região geográfica especificada.
Para executar uma pesquisa local, um aplicativo deve seguir estas etapas:
- Criar
MKLocalSearchRequest
objeto. - Crie um
MKLocalSearch
objeto aMKLocalSearchRequest
partir do . - Chame
MKLocalSearch
oStart
método no objeto. - Recupere o
MKLocalSearchResponse
objeto em um retorno de chamada.
A própria API de pesquisa local não fornece nenhuma interface de usuário. Ele nem precisa de um mapa para ser usado. No entanto, para fazer uso prático da pesquisa local, um aplicativo precisa fornecer alguma maneira de especificar uma consulta de pesquisa e exibir resultados. Além disso, como os resultados conterão dados de localização, muitas vezes fará sentido mostrá-los em um mapa.
Adicionando uma interface do usuário de pesquisa local
Uma maneira de aceitar a entrada de pesquisa é com um UISearchBar
, que fornecido por um UISearchController
e exibirá resultados em uma tabela.
O código a seguir adiciona o UISearchController
(que tem uma propriedade de barra de pesquisa) no ViewDidLoad
método de MapViewController
:
//Creates an instance of a custom View Controller that holds the results
var searchResultsController = new SearchResultsViewController (map);
//Creates a search controller updater
var searchUpdater = new SearchResultsUpdator ();
searchUpdater.UpdateSearchResults += searchResultsController.Search;
//add the search controller
searchController = new UISearchController (searchResultsController) {
SearchResultsUpdater = searchUpdater
};
//format the search bar
searchController.SearchBar.SizeToFit ();
searchController.SearchBar.SearchBarStyle = UISearchBarStyle.Minimal;
searchController.SearchBar.Placeholder = "Enter a search query";
//the search bar is contained in the navigation bar, so it should be visible
searchController.HidesNavigationBarDuringPresentation = false;
//Ensure the searchResultsController is presented in the current View Controller
DefinesPresentationContext = true;
//Set the search bar in the navigation bar
NavigationItem.TitleView = searchController.SearchBar;
Observe que você é responsável por incorporar o objeto da barra de pesquisa na interface do usuário. Neste exemplo, atribuímos ele ao TitleView da barra de navegação, mas se você não usar um controlador de navegação em seu aplicativo, terá que encontrar outro local para exibi-lo.
Neste trecho de código, criamos outro controlador de exibição personalizado – searchResultsController
– que exibe os resultados da pesquisa e, em seguida, usamos esse objeto para criar nosso objeto de controlador de pesquisa. Também criamos um novo atualizador de pesquisa, que se torna ativo quando o usuário interage com a barra de pesquisa. Ele recebe notificações sobre pesquisas a cada pressionamento de tecla e é responsável por atualizar a interface do usuário.
Vamos dar uma olhada em como implementar o searchResultsController
e o searchResultsUpdater
mais tarde neste guia.
Isso resulta em uma barra de pesquisa exibida sobre o mapa, como mostrado abaixo:
Exibindo os resultados da pesquisa
Para exibir os resultados da pesquisa, precisamos criar um controlador de exibição personalizado; normalmente um UITableViewController
. Como mostrado acima, o searchResultsController
é passado para o construtor do searchController
quando ele está sendo criado.
O código a seguir é um exemplo de como criar esse controlador de exibição personalizado:
public class SearchResultsViewController : UITableViewController
{
static readonly string mapItemCellId = "mapItemCellId";
MKMapView map;
public List<MKMapItem> MapItems { get; set; }
public SearchResultsViewController (MKMapView map)
{
this.map = map;
MapItems = new List<MKMapItem> ();
}
public override nint RowsInSection (UITableView tableView, nint section)
{
return MapItems.Count;
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell (mapItemCellId);
if (cell == null)
cell = new UITableViewCell ();
cell.TextLabel.Text = MapItems [indexPath.Row].Name;
return cell;
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
// add item to map
CLLocationCoordinate2D coord = MapItems [indexPath.Row].Placemark.Location.Coordinate;
map.AddAnnotations (new MKPointAnnotation () {
Title = MapItems [indexPath.Row].Name,
Coordinate = coord
});
map.SetCenterCoordinate (coord, true);
DismissViewController (false, null);
}
public void Search (string forSearchString)
{
// create search request
var searchRequest = new MKLocalSearchRequest ();
searchRequest.NaturalLanguageQuery = forSearchString;
searchRequest.Region = new MKCoordinateRegion (map.UserLocation.Coordinate, new MKCoordinateSpan (0.25, 0.25));
// perform search
var localSearch = new MKLocalSearch (searchRequest);
localSearch.Start (delegate (MKLocalSearchResponse response, NSError error) {
if (response != null && error == null) {
this.MapItems = response.MapItems.ToList ();
this.TableView.ReloadData ();
} else {
Console.WriteLine ("local search error: {0}", error);
}
});
}
}
Atualizando os resultados da pesquisa
O SearchResultsUpdater
atua como um mediador entre a barra de pesquisa do e os resultados da searchController
pesquisa.
Neste exemplo, temos que primeiro criar o método de pesquisa no SearchResultsViewController
. Para fazer isso, devemos criar um MKLocalSearch
objeto e usá-lo para emitir uma pesquisa para um MKLocalSearchRequest
, os resultados são recuperados em um retorno de chamada passado para o Start
método do MKLocalSearch
objeto. Os resultados são retornados em um MKLocalSearchResponse
objeto que contém uma matriz de MKMapItem
objetos:
public void Search (string forSearchString)
{
// create search request
var searchRequest = new MKLocalSearchRequest ();
searchRequest.NaturalLanguageQuery = forSearchString;
searchRequest.Region = new MKCoordinateRegion (map.UserLocation.Coordinate, new MKCoordinateSpan (0.25, 0.25));
// perform search
var localSearch = new MKLocalSearch (searchRequest);
localSearch.Start (delegate (MKLocalSearchResponse response, NSError error) {
if (response != null && error == null) {
this.MapItems = response.MapItems.ToList ();
this.TableView.ReloadData ();
} else {
Console.WriteLine ("local search error: {0}", error);
}
});
}
Em seguida, em nosso MapViewController
criaremos uma implementação personalizada do UISearchResultsUpdating
, que é atribuída à SearchResultsUpdater
propriedade de nosso searchController
na seção Adicionando uma interface do usuário de pesquisa local:
public class SearchResultsUpdator : UISearchResultsUpdating
{
public event Action<string> UpdateSearchResults = delegate {};
public override void UpdateSearchResultsForSearchController (UISearchController searchController)
{
this.UpdateSearchResults (searchController.SearchBar.Text);
}
}
A implementação acima adiciona uma anotação ao mapa quando um item é selecionado a partir dos resultados, conforme mostrado abaixo:
Importante
UISearchController
foi implementado no iOS 8. Se você deseja suportar dispositivos anteriores a este, então você precisará usar UISearchDisplayController
o .
Resumo
Este artigo examinou a estrutura do MapKit para iOS. Primeiro, analisou como a MKMapView
classe permite que mapas interativos sejam incluídos em um aplicativo. Em seguida, demonstrou como personalizar ainda mais os mapas usando anotações e sobreposições. Finalmente, examinou os recursos de pesquisa local que foram adicionados ao Map Kit com o iOS 6.1, mostrando como usar para executar consultas baseadas em localização para pontos de interesse e adicioná-los a um mapa.