Карты в Xamarin.iOS
Карты — это общая функция во всех современных мобильных операционных системах. IOS предлагает поддержку сопоставления в собственном коде с помощью платформы Map Kit. С помощью комплекта карт приложения могут легко добавлять расширенные интерактивные карты. Эти карты можно настроить различными способами, например добавление заметок для пометки расположений на карте и перекладывание графики произвольных фигур. Пакет карт даже имеет встроенную поддержку для отображения текущего расположения устройства.
Добавление карты
Добавление карты в приложение выполняется путем добавления экземпляра MKMapView
в иерархию представлений, как показано ниже:
// map is an MKMapView declared as a class variable
map = new MKMapView (UIScreen.MainScreen.Bounds);
View = map;
MKMapView
UIView
— подкласс, отображающий карту. Простое добавление карты с помощью приведенного выше кода создает интерактивную карту:
Стиль карты
MKMapView
поддерживает 3 различных стиля карт. Чтобы применить стиль карты, просто задайте MapType
для свойства значение из MKMapType
перечисления:
map.MapType = MKMapType.Standard; //road map
map.MapType = MKMapType.Satellite;
map.MapType = MKMapType.Hybrid;
На следующем снимку экрана показаны различные стили карт, доступные:
Сдвиг и масштабирование
MKMapView
включает поддержку функций интерактивности карты, таких как:
- Масштабирование с помощью жеста сцепления
- Сдвиг с помощью жеста сдвига
Эти функции можно включить или отключить, просто задав ZoomEnabled
и ScrollEnabled
свойства экземпляра MKMapView
, где значение по умолчанию верно для обоих. Например, чтобы отобразить статическую карту, просто задайте для соответствующих свойств значение false:
map.ZoomEnabled = false;
map.ScrollEnabled = false;
Местонахождение пользователей
Помимо взаимодействия с пользователем, MKMapView
также имеет встроенную поддержку отображения расположения устройства. Это делается с помощью платформы "Основное расположение ". Прежде чем получить доступ к расположению пользователя, необходимо указать пользователю запрос. Для этого создайте экземпляр CLLocationManager
и вызов RequestWhenInUseAuthorization
.
CLLocationManager locationManager = new CLLocationManager();
locationManager.RequestWhenInUseAuthorization();
//locationManager.RequestAlwaysAuthorization(); //requests permission for access to location data while running in the background
Обратите внимание, что в версиях iOS до 8.0 попытка вызова RequestWhenInUseAuthorization
приведет к ошибке. Перед этим вызовом обязательно проверьте версию iOS, если вы планируете поддерживать версии до 8.
Для доступа к расположению пользователя также требуются изменения в Info.plist. Необходимо задать следующие ключи, связанные с данными о расположении:
- NSLocationWhenInUseUsageDescription для доступа к расположению пользователя во время его взаимодействия с приложением.
- NSLocationAlwaysUsageDescription для доступа приложения к расположению пользователя в фоновом режиме.
Эти ключи можно добавить, открыв Info.plist и выбрав источник в нижней части редактора.
После обновления Info.plist и запроса пользователя на доступ к его расположению можно отобразить расположение пользователя на карте, установив ShowsUserLocation
для свойства значение true:
map.ShowsUserLocation = true;
Заметки
MKMapView
также поддерживает отображение изображений, известных как заметки, на карте. Это могут быть пользовательские изображения или системные закрепления различных цветов. Например, на следующем снимка экрана показана карта с закреплением и пользовательским изображением:
Добавление заметки
Сама заметка состоит из двух частей:
- Объект
MKAnnotation
, содержащий данные модели о заметке, например название и расположение заметки. - Объект
MKAnnotationView
, содержащий изображение для отображения и при необходимости выноски, отображаемой при касании заметки пользователем.
Map Kit использует шаблон делегирования MKMapView
iOS для добавления примечаний к карте, где Delegate
свойство объекта задано экземпляромMKMapViewDelegate
. Это реализация этого делегата, которая отвечает за возврат MKAnnotationView
заметки.
Чтобы добавить заметку, сначала добавляется заметка путем вызова AddAnnotations
экземпляра MKMapView
:
// add an annotation
map.AddAnnotations (new MKPointAnnotation (){
Title="MyAnnotation",
Coordinate = new CLLocationCoordinate2D (42.364260, -71.120824)
});
Когда расположение заметки становится видимым на карте, MKMapView
метод делегата GetViewForAnnotation
вызывается для отображения MKAnnotationView
.
Например, следующий код возвращает предоставленный 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;
}
Повторное выполнение заметок
Чтобы сохранить память, MKMapView
можно использовать представление заметки для повторного использования, аналогично тому, как ячейки таблицы повторно используются. Получение представления заметок из пула выполняется с вызовом:DequeueReusableAnnotation
MKAnnotationView pinView = (MKPinAnnotationView)mapView.DequeueReusableAnnotation (pId);
Отображение выносок
Как упоминалось ранее, заметка может при необходимости отображать выноску. Чтобы отобразить выноску, просто установите CanShowCallout
значение true на объекте MKAnnotationView
. Это приводит к отображению заголовка заметки при нажатии заметки, как показано ниже.
Настройка выноски
Выноска также можно настроить для отображения представлений слева и справа, как показано ниже:
pinView.RightCalloutAccessoryView = UIButton.FromType (UIButtonType.DetailDisclosure);
pinView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile ("monkey.png"));
Этот код приводит к следующему выноске:
Чтобы обработать правую аксессуару, просто реализуйте CalloutAccessoryControlTapped
метод в следующих MKMapViewDelegate
элементах:
public override void CalloutAccessoryControlTapped (MKMapView mapView, MKAnnotationView view, UIControl control)
{
...
}
Наложения
Другим способом слоя графики на карте является использование наложений. Наложения позволяют рисовать графическое содержимое, которое масштабируется вместе с картой. IOS обеспечивает поддержку нескольких типов наложений, в том числе:
- Многоугольники — обычно используются для выделения некоторых регионов на карте.
- Polylines - Часто видно при отображении маршрута.
- Круги — используются для выделения круговой области карты.
Кроме того, пользовательские наложения можно создать для отображения произвольных геометрий с детализированный настраиваемый код рисования. Например, погодный радар будет хорошим кандидатом для пользовательского наложения.
Добавление наложения
Аналогично заметкам, добавление наложения включает в себя 2 части:
- Создание объекта модели для наложения и его добавление в
MKMapView
него. - Создание представления для наложения в элементе
MKMapViewDelegate
.
Модель для наложения может быть любым MKShape
подклассом. Xamarin.iOS включает MKShape
подклассы для многоугольников, полилайнов и кругов, через MKPolygon
MKPolyline
классы соответственноMKCircle
.
Например, следующий код используется для добавления MKCircle
:
var circleOverlay = MKCircle.Circle (mapCenter, 1000);
map.AddOverlay (circleOverlay);
Представление для наложения — это MKOverlayView
экземпляр, возвращаемый GetViewForOverlay
в объекте MKMapViewDelegate
. Каждый MKShape
имеет соответствующий MKOverlayView
, который знает, как отобразить заданную фигуру. Для MKPolygon
этого есть MKPolygonView
. Аналогично, MKPolyline
соответствует и для MKCircle
этого естьMKCircleView
MKPolylineView
.
Например, следующий код возвращает MKCircleView
значение для 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;
}
Откроется круг на карте, как показано ниже.
Локальный поиск
IOS включает локальный API поиска с пакетом map, который позволяет асинхронно искать точки интереса в указанном географическом регионе.
Чтобы выполнить локальный поиск, приложение должно выполнить следующие действия:
- Создайте объект
MKLocalSearchRequest
. MKLocalSearch
Создайте объект изMKLocalSearchRequest
объекта .Start
Вызовите метод объектаMKLocalSearch
.MKLocalSearchResponse
Получение объекта в обратном вызове.
Сам API локального поиска не предоставляет пользовательского интерфейса. Он даже не требует использования карты. Однако для практического использования локального поиска приложение должно предоставить какой-то способ указать поисковый запрос и отобразить результаты. Кроме того, так как результаты будут содержать данные о расположении, они часто будут отображаться на карте.
Добавление пользовательского интерфейса локального поиска
Один из способов принять входные данные поиска — с UISearchBar
помощью функции, предоставленной и UISearchController
отображающей результаты в таблице.
Следующий код добавляет UISearchController
(которое имеет свойство панели поиска) в ViewDidLoad
методе 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;
Обратите внимание, что вы несете ответственность за включение объекта панели поиска в пользовательский интерфейс. В этом примере мы назначили его в TitleView панели навигации, но если вы не используете контроллер навигации в приложении, вам придется найти другое место для его отображения.
В этом фрагменте кода мы создали другой контроллер пользовательского представления , searchResultsController
который отображает результаты поиска, а затем мы использовали этот объект для создания объекта контроллера поиска. Мы также создали новый обработчик обновления поиска, который становится активным при взаимодействии пользователя с строкой поиска. Он получает уведомления о поиске с каждым нажатием клавиш и отвечает за обновление пользовательского интерфейса.
Мы рассмотрим, как реализовать как в этом руководстве, searchResultsUpdater
так searchResultsController
и далее.
Это приводит к отображению панели поиска на карте, как показано ниже:
Отображение результатов поиска
Чтобы отобразить результаты поиска, необходимо создать пользовательский контроллер представления; обычно .UITableViewController
Как показано выше, конструктор searchResultsController
передается конструктору searchController
при его создании.
Следующий код является примером создания этого пользовательского контроллера представления:
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);
}
});
}
}
Обновление результатов поиска
Он SearchResultsUpdater
выступает в качестве посредника между searchController
строкой поиска и результатами поиска.
В этом примере сначала необходимо создать метод поиска в .SearchResultsViewController
Для этого необходимо создать MKLocalSearch
объект и использовать его для выдачи поиска MKLocalSearchRequest
, результаты извлекаются в обратном вызове, переданном Start
методу MKLocalSearch
объекта. Затем результаты возвращаются в объекте MKLocalSearchResponse
, содержав массив MKMapItem
объектов:
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);
}
});
}
Затем мы MapViewController
создадим настраиваемую реализацию UISearchResultsUpdating
, которая назначается SearchResultsUpdater
свойству нашего searchController
раздела "Добавление локального пользовательского интерфейса поиска":
public class SearchResultsUpdator : UISearchResultsUpdating
{
public event Action<string> UpdateSearchResults = delegate {};
public override void UpdateSearchResultsForSearchController (UISearchController searchController)
{
this.UpdateSearchResults (searchController.SearchBar.Text);
}
}
Приведенная выше реализация добавляет заметку к карте при выборе элемента из результатов, как показано ниже:
Внимание
UISearchController
реализован в iOS 8. Если вы хотите поддерживать устройства раньше этого, вам потребуется использовать UISearchDisplayController
.
Итоги
В этой статье рассматривается платформа пакета карт для iOS. Во-первых, он рассмотрел, как MKMapView
класс позволяет интерактивным картам включаться в приложение. Затем он продемонстрировал, как дополнительно настроить карты с помощью заметок и наложений. Наконец, она изучила возможности локального поиска, добавленные в Пакет карт с iOS 6.1, в котором показано, как использовать запросы на основе расположения для точек интереса и добавить их на карту.