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
實例的 MKMapView
和 ScrollEnabled
屬性,即可啟用或停用這些功能,其中兩者的預設值為 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 委派模式將註釋新增至地圖,其中 Delegate
的 MKMapView
屬性會設定為 的 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"));
此程式代碼會產生下列註標:
若要處理用戶點選正確的配件,只需在 中實 CalloutAccessoryControlTapped
作 MKMapViewDelegate
方法:
public override void CalloutAccessoryControlTapped (MKMapView mapView, MKAnnotationView view, UIControl control)
{
...
}
覆蓋
地圖上圖層圖形的另一種方式是使用重疊。 重疊支援繪製隨地圖縮放比例的圖形內容。 iOS 支援數種類型的重疊,包括:
- 多邊形 - 通常用來在地圖上反白顯示某些區域。
- 聚合線條 - 顯示路線時經常看到。
- 圓形 - 用來反白顯示地圖的圓形區域。
此外,您可以建立自定義重疊來顯示具有細微、自定義繪圖程序代碼的任意幾何。 例如,天氣雷達是自定義重疊的好候選專案。
新增重疊
與註釋類似,新增重疊牽涉到2個部分:
- 建立覆迭的模型物件,並將其新增至
MKMapView
。 - 在中建立重疊的
MKMapViewDelegate
檢視。
重疊的模型可以是任何 MKShape
子類別。 Xamarin.iOS 分別透過、 MKPolyline
和 MKCircle
類別,包含MKShape
多邊形、聚合線條和圓形MKPolygon
的子類別。
例如,下列程式代碼可用來新增 MKCircle
:
var circleOverlay = MKCircle.Circle (mapCenter, 1000);
map.AddOverlay (circleOverlay);
重疊的檢視是由 MKOverlayView
中 傳 GetViewForOverlay
回的 MKMapViewDelegate
實例。 每個 MKShape
都有一個對應 MKOverlayView
,知道如何顯示指定的圖形。 針對 MKPolygon
有 MKPolygonView
。 同樣地, MKPolyline
對應至 MKPolylineView
,而 針對 MKCircle
有 MKCircleView
。
例如,下列程式代碼會 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,可讓異步搜尋指定地理區域中的景點。
若要執行本機搜尋,應用程式必須遵循下列步驟:
- 建立
MKLocalSearchRequest
物件。 MKLocalSearch
從MKLocalSearchRequest
建立物件。Start
在物件上MKLocalSearch
呼叫 方法。- 在回呼中擷
MKLocalSearchResponse
取物件。
本機搜尋 API 本身不提供任何使用者介面。 它甚至不需要使用地圖。 不過,若要實際使用本機搜尋,應用程式必須提供某種方式來指定搜尋查詢並顯示結果。 此外,由於結果會包含位置數據,因此在地圖上顯示它們通常很合理。
新增本機搜尋UI
接受搜尋輸入的其中一 UISearchBar
種方式是 ,其由 提供 UISearchController
,並將在數據表中顯示結果。
下列程式代碼會在 的 方法MapViewController
中ViewDidLoad
新增 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 的 Map Kit 架構 。 首先,它探討 類別 MKMapView
如何允許將互動式地圖包含在應用程式中。 然後,它示範如何使用註釋和重迭進一步自定義地圖。 最後,它檢查了已新增至 iOS 6.1 地圖套件的本機搜尋功能,顯示如何使用位置型查詢來尋找景點,並將其新增至地圖。