Udostępnij za pośrednictwem


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:

Przykładowa mapa

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:

Ten 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;

Alert dotyczący zezwalania na dostęp do lokalizacji

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:

Ten 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 MKPinAnnotationViewprzez 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:

Wyświetlany tytuł adnotacji

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:

Przykładowe 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 MKPolygonMKPolyline 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 MKCircleViewwartość .

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:

Okrąg wyświetlany na mapie

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:

  1. Utwórz MKLocalSearchRequest obiekt.
  2. MKLocalSearch Utwórz obiekt na podstawie obiektu MKLocalSearchRequest .
  3. Wywołaj metodę StartMKLocalSearch w obiekcie .
  4. 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 klawiszy 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:

Pasek wyszukiwania wyświetlany na mapie

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 searchControllerpaskiem 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 MKLocalSearchRequestelementu , 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ę UISearchResultsUpdatingelementu , 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:

Adnotacja dodana do mapy po wybraniu elementu z wyników

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 MapKit 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.