Share via


Xamarin.iOS 中的 地圖

地圖 是所有新式行動操作系統中的常見功能。 iOS 透過 Map Kit 架構原生提供對應支援。 透過 Map Kit,應用程式可以輕鬆地新增豐富的互動式地圖。 這些地圖可以透過各種方式自定義,例如新增註釋來標記地圖上的位置,以及重疊任意圖形的圖形。 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 實例的 MKMapViewScrollEnabled 屬性,即可啟用或停用這些功能,其中兩者的預設值為 true。 例如,若要顯示靜態地圖,只需將適當的屬性設定為 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

請注意,在 8.0 之前的 iOS 版本中,嘗試呼叫 RequestWhenInUseAuthorization 會導致錯誤。 如果您想要支援 8 之前的版本,請務必先檢查 iOS 的版本,再進行該呼叫。

存取使用者的位置也需要修改 Info.plist。 以下是應該設定的位置資料相關機碼:

  • NSLocationWhenInUseUsageDescription - 用於當您在使用者與應用程式互動期間存取使用者位置時。
  • NSLocationAlwaysUsageDescription - 用於您的應用程式在背景中存取使用者的位置時。

您可以開啟 Info.plist 並選取 編輯器底部的 [來源 ],以新增這些密鑰。

更新 Info.plist 並提示使用者存取其位置的許可權後,您可以將 屬性設定 ShowsUserLocation 為 true,在地圖上顯示使用者的位置:

map.ShowsUserLocation = true;

允許位置存取警示

註釋

MKMapView 也支援在地圖上顯示稱為註釋的影像。 這些可以是自定義映像或各種色彩的系統定義針腳。 例如,下列螢幕快照顯示具有釘選和自定義影像的地圖:

此螢幕快照顯示具有釘選和自定義影像的地圖

新增批注

註釋本身有兩個部分:

  • 物件 MKAnnotation ,其中包含註釋的相關模型數據,例如批註的標題和位置。
  • MKAnnotationView ,其中包含要顯示的影像,並選擇性地顯示當用戶點選批注時顯示的圖說文字。

Map Kit 會使用 iOS 委派模式將註釋新增至地圖,其中 DelegateMKMapView 屬性會設定為 的 MKMapViewDelegate實例。 這是此委派的實作,負責傳 MKAnnotationView 回註釋的 。

若要新增批注,請先在 實例上MKMapView呼叫 AddAnnotations 來新增批注:

// 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);

顯示圖說文字

如先前所述,註釋可以選擇性地顯示圖說文字。 若要顯示圖說文字,只要在 上MKAnnotationView設定CanShowCallout為 true 即可。 這會在點選批註時顯示批註的標題,如下所示:

正在顯示的批註標題

自定義圖說文字

圖說文字也可以自定義以顯示左右配件檢視,如下所示:

pinView.RightCalloutAccessoryView = UIButton.FromType (UIButtonType.DetailDisclosure);
pinView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile ("monkey.png"));

此程式代碼會產生下列註標:

圖說文字範例

若要處理用戶點選正確的配件,只需在 中實 CalloutAccessoryControlTappedMKMapViewDelegate方法:

public override void CalloutAccessoryControlTapped (MKMapView mapView, MKAnnotationView view, UIControl control)
{
    ...
}

覆蓋

地圖上圖層圖形的另一種方式是使用重疊。 重疊支援繪製隨地圖縮放比例的圖形內容。 iOS 支援數種類型的重疊,包括:

  • 多邊形 - 通常用來在地圖上反白顯示某些區域。
  • 聚合線條 - 顯示路線時經常看到。
  • 圓形 - 用來反白顯示地圖的圓形區域。

此外,您可以建立自定義重疊來顯示具有細微、自定義繪圖程序代碼的任意幾何。 例如,天氣雷達是自定義重疊的好候選專案。

新增重疊

與註釋類似,新增重疊牽涉到2個部分:

  • 建立覆迭的模型物件,並將其新增至 MKMapView
  • 在中建立重疊的 MKMapViewDelegate 檢視。

重疊的模型可以是任何 MKShape 子類別。 Xamarin.iOS 分別透過、 MKPolylineMKCircle 類別,包含MKShape多邊形、聚合線條和圓形MKPolygon的子類別。

例如,下列程式代碼可用來新增 MKCircle

var circleOverlay = MKCircle.Circle (mapCenter, 1000);
map.AddOverlay (circleOverlay);

重疊的檢視是由 MKOverlayView 中 傳 GetViewForOverlay 回的 MKMapViewDelegate實例。 每個 MKShape 都有一個對應 MKOverlayView ,知道如何顯示指定的圖形。 針對 MKPolygonMKPolygonView。 同樣地, MKPolyline 對應至 MKPolylineView,而 針對 MKCircleMKCircleView

例如,下列程式代碼會 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 包含具有 Map Kit 的本機搜尋 API,可讓異步搜尋指定地理區域中的景點。

若要執行本機搜尋,應用程式必須遵循下列步驟:

  1. 建立 MKLocalSearchRequest 物件。
  2. MKLocalSearchMKLocalSearchRequest 建立物件。
  3. Start在物件上MKLocalSearch呼叫 方法。
  4. 在回呼中擷 MKLocalSearchResponse 取物件。

本機搜尋 API 本身不提供任何使用者介面。 它甚至不需要使用地圖。 不過,若要實際使用本機搜尋,應用程式必須提供某種方式來指定搜尋查詢並顯示結果。 此外,由於結果會包含位置數據,因此在地圖上顯示它們通常很合理。

新增本機搜尋UI

接受搜尋輸入的其中一 UISearchBar種方式是 ,其由 提供 UISearchController ,並將在數據表中顯示結果。

下列程式代碼會在 的 方法MapViewControllerViewDidLoad新增 UISearchController (其具有搜尋欄位屬性) :

//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 顯示搜尋結果,然後使用這個物件來建立搜尋控制器物件。 我們也建立了新的搜尋更新程式,當使用者與搜尋列互動時,就會變成作用中。 它會接收有關每個擊鍵搜尋的通知,並負責更新UI。 我們將探討如何實 searchResultsController 作 本指南稍後的 和 searchResultsUpdater

這會在地圖上顯示搜尋列,如下所示:

在地圖上顯示的搜尋列

顯示搜尋結果

若要顯示搜尋結果,我們需要建立自定義檢視控制器;通常是 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 回呼中擷取。 然後,結果會在包含 物件陣列的 MKMapItem 物件中MKLocalSearchResponse傳回:

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 我們在 [新增本機搜尋 UI] 區段中的 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 的 MapKit 架構 。 首先,它探討 類別 MKMapView 如何允許將互動式地圖包含在應用程式中。 然後,它示範如何使用註釋和重迭進一步自定義地圖。 最後,它檢查了已新增至 iOS 6.1 地圖套件的本機搜尋功能,顯示如何使用位置型查詢來尋找景點,並將其新增至地圖。