Mapy w środowisku Xamarin.iOS
Mapy są wspólną funkcją we wszystkich nowoczesnych systemach operacyjnych mobilnych. System iOS oferuje natywną obsługę mapowania za pośrednictwem struktury zestawu Map Kit. Dzięki zestawowi Map Kit aplikacje mogą łatwo dodawać rozbudowane, interaktywne mapy. Mapy te można dostosować na różne sposoby, takie jak dodawanie adnotacji do oznaczania lokalizacji na mapie i nakładanie grafiki dowolnych kształtów. Zestaw Map Kit ma nawet wbudowaną obsługę wyświetlania bieżącej lokalizacji urządzenia.
Dodawanie mapy
Dodanie mapy do aplikacji odbywa się przez dodanie MKMapView
wystąpienia do hierarchii widoków, jak pokazano poniżej:
// map is an MKMapView declared as a class variable
map = new MKMapView (UIScreen.MainScreen.Bounds);
View = map;
MKMapView
to podklasa UIView
, która wyświetla mapę. Po prostu dodanie mapy przy użyciu powyższego kodu powoduje utworzenie interaktywnej mapy:
Styl mapy
MKMapView
obsługuje 3 różne style map. Aby zastosować styl mapy, po prostu ustaw MapType
właściwość na wartość z MKMapType
wyliczenia:
map.MapType = MKMapType.Standard; //road map
map.MapType = MKMapType.Satellite;
map.MapType = MKMapType.Hybrid;
Poniższy zrzut ekranu przedstawia różne dostępne style mapy:
Przesuwanie i powiększanie
MKMapView
Obejmuje obsługę funkcji interakcyjności mapy, takich jak:
- Powiększanie za pomocą gestu szczypta
- Przesuwanie za pomocą gestu patelni
Te funkcje można włączyć lub wyłączyć, ustawiając ZoomEnabled
właściwości MKMapView
i ScrollEnabled
wystąpienia, gdzie wartość domyślna jest prawdziwa dla obu tych funkcji. Aby na przykład wyświetlić mapę statyczną, wystarczy ustawić odpowiednie właściwości na wartość false:
map.ZoomEnabled = false;
map.ScrollEnabled = false;
Lokalizacja użytkowników
Oprócz interakcji z użytkownikiem jest MKMapView
również wbudowana obsługa wyświetlania lokalizacji urządzenia. Robi to przy użyciu platformy Lokalizacja podstawowa. Przed uzyskaniem dostępu do lokalizacji użytkownika należy wyświetlić monit o podanie monitu. W tym celu utwórz wystąpienie CLLocationManager
elementu i wywołaj metodę RequestWhenInUseAuthorization
.
CLLocationManager locationManager = new CLLocationManager();
locationManager.RequestWhenInUseAuthorization();
//locationManager.RequestAlwaysAuthorization(); //requests permission for access to location data while running in the background
Pamiętaj, że w wersjach systemu iOS wcześniejszych niż 8.0 próba wywołania RequestWhenInUseAuthorization
spowoduje wystąpienie błędu. Pamiętaj, aby sprawdzić wersję systemu iOS przed wykonaniem tego wywołania, jeśli zamierzasz obsługiwać wersje wcześniejsze niż 8.
Uzyskanie dostępu do lokalizacji użytkownika wymaga również modyfikacji pliku Info.plist. Należy ustawić następujące klucze odnoszące się do danych lokalizacji:
- NSLocationWhenInUseUsageDescription — w przypadku uzyskiwania dostępu do lokalizacji użytkownika podczas interakcji z aplikacją.
- NSLocationAlwaysUsageDescription — w przypadku, gdy aplikacja uzyskuje dostęp do lokalizacji użytkownika w tle.
Możesz dodać te klucze, otwierając plik Info.plist i wybierając pozycję Źródło w dolnej części edytora.
Po zaktualizowaniu pliku Info.plist i wyświetleniu użytkownikowi monitu o zezwolenie na dostęp do lokalizacji użytkownika możesz wyświetlić lokalizację użytkownika na mapie, ustawiając ShowsUserLocation
właściwość na wartość true:
map.ShowsUserLocation = true;
Adnotacje
MKMapView
Obsługuje również wyświetlanie obrazów, znanych jako adnotacje, na mapie. Mogą to być obrazy niestandardowe lub wyprowadzenia zdefiniowane przez system różnych kolorów. Na przykład poniższy zrzut ekranu przedstawia mapę z przypięciem i obrazem niestandardowym:
Dodawanie adnotacji
Sama adnotacja ma dwie części:
- Obiekt
MKAnnotation
, który zawiera dane modelu dotyczące adnotacji, takie jak tytuł i lokalizacja adnotacji. - Element
MKAnnotationView
, który zawiera obraz do wyświetlenia i opcjonalnie objaśnienie wyświetlane po naciśnięciu adnotacji przez użytkownika.
Zestaw Map Kit używa wzorca delegowania systemu iOS do dodawania adnotacji do mapy, gdzie Delegate
właściwość MKMapView
obiektu jest ustawiona na wystąpienie elementu MKMapViewDelegate
. Jest to implementacja tego delegata, która jest odpowiedzialna za zwracanie MKAnnotationView
adnotacji.
Aby dodać adnotację, najpierw adnotacja zostanie dodana przez wywołanie AddAnnotations
wystąpienia MKMapView
:
// add an annotation
map.AddAnnotations (new MKPointAnnotation (){
Title="MyAnnotation",
Coordinate = new CLLocationCoordinate2D (42.364260, -71.120824)
});
Gdy lokalizacja adnotacji stanie się widoczna na mapie, MKMapView
metoda wywoła metodę delegata GetViewForAnnotation
, aby wyświetlić element MKAnnotationView
.
Na przykład następujący kod zwraca podany MKPinAnnotationView
przez system element :
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;
}
Ponowne dodawanie adnotacji
Aby zaoszczędzić pamięć, MKMapView
umożliwia buforowanie widoku adnotacji do ponownego użycia, podobnie jak w przypadku ponownego użycia komórek tabeli. Uzyskiwanie widoku adnotacji z puli odbywa się za pomocą wywołania metody DequeueReusableAnnotation
:
MKAnnotationView pinView = (MKPinAnnotationView)mapView.DequeueReusableAnnotation (pId);
Wyświetlanie objaśnień
Jak wspomniano wcześniej, adnotacja może opcjonalnie wyświetlać objaśnienie. Aby wyświetlić objaśnienie, po prostu ustaw CanShowCallout
wartość true na MKAnnotationView
. Spowoduje to wyświetlenie tytułu adnotacji po naciśnięciu adnotacji, jak pokazano poniżej:
Dostosowywanie objaśnienia
Objaśnienie można również dostosować do wyświetlania widoków akcesoriów po lewej i prawej stronie, jak pokazano poniżej:
pinView.RightCalloutAccessoryView = UIButton.FromType (UIButtonType.DetailDisclosure);
pinView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile ("monkey.png"));
Ten kod powoduje następujące objaśnienie:
Aby obsłużyć użytkownika naciskając odpowiednie metody dostępu, po prostu zaimplementuj metodę CalloutAccessoryControlTapped
w pliku MKMapViewDelegate
:
public override void CalloutAccessoryControlTapped (MKMapView mapView, MKAnnotationView view, UIControl control)
{
...
}
Nakładki
Innym sposobem warstwowania grafiki na mapie jest użycie nakładek. Nakładki obsługują rysowanie zawartości graficznej, która skaluje się z mapą w miarę powiększania. System iOS zapewnia obsługę kilku typów nakładek, w tym:
- Wielokąty — często używane do wyróżniania niektórych regionów na mapie.
- Linie wielokątne — często widoczne podczas pokazywania trasy.
- Okręgi — służy do wyróżniania okrągłego obszaru mapy.
Ponadto można tworzyć niestandardowe nakładki w celu wyświetlania dowolnych geometrii z szczegółowym, dostosowanym kodem rysunkowym. Na przykład radar pogody byłby dobrym kandydatem do niestandardowej nakładki.
Dodawanie nakładki
Podobnie jak adnotacje, dodanie nakładki obejmuje 2 części:
- Utworzenie obiektu modelu dla nakładki i dodanie go do obiektu
MKMapView
. - Tworzenie widoku nakładki w obiekcie
MKMapViewDelegate
.
Model nakładki może być dowolną MKShape
podklasą. Platforma Xamarin.iOS zawiera MKShape
podklasy dla wielokątów, wielolinii i okręgów, odpowiednio za pośrednictwem MKPolygon
MKPolyline
klas i MKCircle
.
Na przykład następujący kod służy do dodawania elementu MKCircle
:
var circleOverlay = MKCircle.Circle (mapCenter, 1000);
map.AddOverlay (circleOverlay);
Widok nakładki jest wystąpieniem MKOverlayView
zwracanym przez GetViewForOverlay
element w pliku MKMapViewDelegate
. Każdy z nich MKShape
ma odpowiedni MKOverlayView
element, który wie, jak wyświetlić dany kształt. W przypadku MKPolygon
elementu znajduje się MKPolygonView
. MKPolyline
Podobnie, odpowiada parametrowi MKPolylineView
, a dla MKCircle
parametru istnieje MKCircleView
wartość .
Na przykład następujący kod zwraca element MKCircleView
dla elementu 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;
}
Spowoduje to wyświetlenie okręgu na mapie, jak pokazano poniżej:
Wyszukiwanie lokalne
System iOS zawiera lokalny interfejs API wyszukiwania z zestawem Map Kit, który umożliwia asynchroniczne wyszukiwanie punktów orientacyjnych w określonym regionie geograficznym.
Aby przeprowadzić wyszukiwanie lokalne, aplikacja musi wykonać następujące kroki:
- Utwórz
MKLocalSearchRequest
obiekt. MKLocalSearch
Utwórz obiekt na podstawie obiektuMKLocalSearchRequest
.- Wywołaj metodę
Start
MKLocalSearch
w obiekcie . MKLocalSearchResponse
Pobierz obiekt w wywołaniu zwrotnym.
Interfejs API wyszukiwania lokalnego nie zapewnia interfejsu użytkownika. Nie wymaga nawet użycia mapy. Jednak w celu praktycznego użycia wyszukiwania lokalnego aplikacja musi zapewnić pewien sposób określania zapytania wyszukiwania i wyświetlania wyników. Ponadto, ponieważ wyniki będą zawierać dane lokalizacji, często warto je pokazać na mapie.
Dodawanie lokalnego interfejsu użytkownika wyszukiwania
Jednym ze sposobów akceptowania danych wejściowych wyszukiwania jest element UISearchBar
, który jest dostarczany przez UISearchController
element i spowoduje wyświetlenie wyników w tabeli.
Poniższy kod dodaje właściwość UISearchController
(która ma właściwość paska wyszukiwania) w ViewDidLoad
metodzie :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;
Należy pamiętać, że odpowiadasz za włączenie obiektu paska wyszukiwania do interfejsu użytkownika. W tym przykładzie przypisano go do kontrolki TitleView paska nawigacyjnego, ale jeśli nie używasz kontrolera nawigacji w aplikacji, musisz znaleźć inne miejsce do jego wyświetlenia.
W tym fragmencie kodu utworzyliśmy inny niestandardowy kontroler widoku — searchResultsController
wyświetlający wyniki wyszukiwania, a następnie użyliśmy tego obiektu do utworzenia obiektu kontrolera wyszukiwania. Utworzyliśmy również nowy aktualizator wyszukiwania, który staje się aktywny, gdy użytkownik wchodzi w interakcję z paskiem wyszukiwania. Otrzymuje powiadomienia o wyszukiwaniu przy każdym naciśnięciu i jest odpowiedzialny za aktualizowanie interfejsu użytkownika.
Przyjrzymy się, jak zaimplementować zarówno instrukcje, searchResultsController
jak i searchResultsUpdater
w dalszej części tego przewodnika.
Spowoduje to wyświetlenie paska wyszukiwania na mapie, jak pokazano poniżej:
Wyświetlanie wyników wyszukiwania
Aby wyświetlić wyniki wyszukiwania, musimy utworzyć niestandardowy kontroler widoku; zwykle .UITableViewController
Jak pokazano powyżej, element searchResultsController
jest przekazywany do konstruktora searchController
podczas jego tworzenia.
Poniższy kod to przykład tworzenia niestandardowego kontrolera widoku:
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);
}
});
}
}
Aktualizowanie wyników wyszukiwania
Działa SearchResultsUpdater
jako mediator między searchController
paskiem wyszukiwania a wynikami wyszukiwania.
W tym przykładzie musimy najpierw utworzyć metodę wyszukiwania w pliku SearchResultsViewController
. W tym celu musimy utworzyć MKLocalSearch
obiekt i użyć go do wystawienia wyszukiwania MKLocalSearchRequest
elementu , wyniki są pobierane w wywołaniu zwrotnym przekazanym do Start
metody MKLocalSearch
obiektu. Wyniki są następnie zwracane w MKLocalSearchResponse
obiekcie zawierającym tablicę MKMapItem
obiektów:
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);
}
});
}
Następnie w naszej MapViewController
sekcji utworzymy niestandardową implementację UISearchResultsUpdating
elementu , która jest przypisana do SearchResultsUpdater
właściwości naszej searchController
w sekcji Dodawanie lokalnego interfejsu użytkownika wyszukiwania:
public class SearchResultsUpdator : UISearchResultsUpdating
{
public event Action<string> UpdateSearchResults = delegate {};
public override void UpdateSearchResultsForSearchController (UISearchController searchController)
{
this.UpdateSearchResults (searchController.SearchBar.Text);
}
}
W powyższej implementacji dodano adnotację do mapy po wybraniu elementu z wyników, jak pokazano poniżej:
Ważne
UISearchController
został zaimplementowany w systemie iOS 8. Jeśli chcesz obsługiwać urządzenia wcześniejsze niż to, musisz użyć polecenia UISearchDisplayController
.
Podsumowanie
W tym artykule zbadano strukturę zestawu Map Kit dla systemu iOS. Najpierw przyjrzeliśmy się temu, jak MKMapView
klasa umożliwia dołączanie interakcyjnych map do aplikacji. Następnie pokazano, jak dalej dostosowywać mapy przy użyciu adnotacji i nakładek. Na koniec zbadano możliwości wyszukiwania lokalnego, które zostały dodane do zestawu Map Kit z systemem iOS 6.1, pokazując, jak używać zapytań opartych na lokalizacji dla punktów orientacyjnych i dodać je do mapy.