Standford 2015 week9

62
Standford 2015 iOS讀書會 week9 1. MapKit 2. Modal(and Popover) segues 彼得潘

Transcript of Standford 2015 week9

Page 1: Standford 2015 week9

Standford 2015 iOS讀書會 week9

1. MapKit 2. Modal(and Popover) segues

彼得潘

Page 2: Standford 2015 week9

map view & annotation

Page 3: Standford 2015 week9

callout

Page 4: Standford 2015 week9

如果定義⾃自⼰己的MKAnnotationView類別,則可客製化callouthttps://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/

LocationAwarenessPG/AnnotatingMaps/AnnotatingMaps.html

點選callout

Page 5: Standford 2015 week9

建⽴立MKMapView

Page 6: Standford 2015 week9

顯⽰示地圖的crash陷阱

Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'Could not instantiate class named MKMapView'

Page 7: Standford 2015 week9

link MapKit framework

從程式加⼊入MKMapView時, import MapKit.h會⾃自動連結MapKit framework

Page 8: Standford 2015 week9
Page 9: Standford 2015 week9
Page 10: Standford 2015 week9
Page 11: Standford 2015 week9
Page 12: Standford 2015 week9
Page 13: Standford 2015 week9
Page 14: Standford 2015 week9

var userLocationVisible: Bool { get }

Page 15: Standford 2015 week9

地圖上顯⽰示使⽤用者位置

Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.

@property (nonatomic) BOOL showsUserLocation; 需先取得使⽤用者同意

Page 16: Standford 2015 week9
Page 17: Standford 2015 week9

讓地圖⾃自動縮放到⺫⽬目前使⽤用者的位置

func mapView(mapView: MKMapView!, didUpdateUserLocation userLocation: MKUserLocation!) { if isFirstGetLocation == false { isFirstGetLocation = true let span = MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005) let region = MKCoordinateRegion(center: userLocation.location.coordinate, span: span); mapView.region = region;

} } delta: 兩邊的經度差或緯度差

Page 18: Standford 2015 week9

讓地圖⾃自動縮放到⺫⽬目前使⽤用者的位置

mapView.region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 1000, 1000 );

Page 19: Standford 2015 week9

動畫縮放地圖範圍

let region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 1000, 1000 ); self.mapView.setRegion(region, animated: true)

Page 20: Standford 2015 week9

以使⽤用者位置為 地圖中⼼心點

var centerCoordinate: CLLocationCoordinate2D

func setCenterCoordinate(coordinate: CLLocationCoordinate2D, animated: Bool)

Page 21: Standford 2015 week9
Page 22: Standford 2015 week9
Page 23: Standford 2015 week9
Page 24: Standford 2015 week9
Page 25: Standford 2015 week9

let req = MKLocalSearchRequest() req.naturalLanguageQuery = "誠品" let search = MKLocalSearch(request: req) search.startWithCompletionHandler { (response:MKLocalSearchResponse!, err:NSError!) -> Void in for mapItem in response.mapItems as! [MKMapItem] { println("\(mapItem.name) \(mapItem.placemark.addressDictionary)")

} }誠品忠誠店 [Street: 忠誠路⼆二段188號, SubAdministrativeArea: 臺北市, State: 台北, SubThoroughfare: 188, CountryCode: TW, ZIP: 111, Thoroughfare: 忠誠路⼆二段,

Name: 誠品忠誠店, Country: 台灣, FormattedAddressLines: ( "111\U53f0\U7063\U81fa\U5317\U5e02\U58eb\U6797\U5340\U5fe0\U8aa0\U8def\U4e8c\U6bb5188\U865f" ), City: ⼠士林區] 誠品 [SubAdministrativeArea: 新北市, CountryCode: TW, SubLocality: 板橋區, State: 新北市, Street: 縣⺠民⼤大道⼆二段66號, ZIP: 220, Name: 誠品, Thoroughfare: 縣⺠民⼤大道⼆二段, FormattedAddressLines: ( "220\U53f0\U7063\U65b0\U5317\U5e02\U677f\U6a4b\U5340\U7e23\U6c11\U5927\U9053\U4e8c\U6bb566\U865f" ), SubThoroughfare: 66, Country: 台灣, City: 新北市] 誠品書店 [SubAdministrativeArea: 新北市, CountryCode: TW, SubLocality: 板橋區, State: 新北市, Street: 中⼭山路⼀一段46號, ZIP: 220, Name: 誠品書店, Thoroughfare: 中⼭山路⼀一段, FormattedAddressLines: ( "220\U53f0\U7063\U65b0\U5317\U5e02\U677f\U6a4b\U5340\U4e2d\U5c71\U8def\U4e00\U6bb546\U865f" ), SubThoroughfare: 46, Country: 台灣, City: 新北市] 誠品 [Street: 中⼭山南路7號, SubAdministrativeArea: 臺北市, State: 台北, SubThoroughfare: 7, CountryCode: TW, ZIP: 100, Thoroughfare: 中⼭山南路, Name: 誠品, Country: 台灣, FormattedAddressLines: ( "100\U53f0\U7063\U81fa\U5317\U5e02\U4e2d\U6b63\U5340\U4e2d\U5c71\U5357\U8def7\U865f" ), City: 中正區] 誠品書店 [Street: 敦化南路⼀一段245號, SubAdministrativeArea: 臺北市, State: 台北, SubThoroughfare: 245, CountryCode: TW, ZIP: 106, Thoroughfare: 敦化南路⼀一段, Name: 誠品書店, Country: 台灣, FormattedAddressLines: ( "106\U53f0\U7063\U81fa\U5317\U5e02\U5927\U5b89\U5340\U6566\U5316\U5357\U8def\U4e00\U6bb5245\U865f" ), City: ⼤大安區] 誠品-站前店 [Street: 忠孝⻄西路⼀一段47號, SubAdministrativeArea: 臺北市, State: 台北, SubThoroughfare: 47, CountryCode: TW, ZIP: 100, Thoroughfare: 忠孝⻄西路⼀一段, Name: 誠品-站前店, Country: 台灣, FormattedAddressLines: ( "100\U53f0\U7063\U81fa\U5317\U5e02\U4e2d\U6b63\U5340\U5fe0\U5b5d\U897f\U8def\U4e00\U6bb547\U865f" ), City: 中正區]

Page 26: Standford 2015 week9
Page 27: Standford 2015 week9

打開地圖let place = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 25.039489, longitude: 121.549328), addressDictionary: nil) let destination = MKMapItem(placemark: place) destination.name = "敦南誠品" let items = [destination] MKMapItem.openMapsWithItems(items, launchOptions: nil)

Page 28: Standford 2015 week9

Apple Map查經緯度

http://maps.apple.com/?lsp=57879&auid=1018420676803274&sll=25.039489,121.549

328&q=誠%5C%5C%5C品書店%28敦南⾳音樂館%29

1 2

3

4

Page 29: Standford 2015 week9

導航:以⺫⽬目前所在位置為起點let place = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 25.039489, longitude: 121.549328), addressDictionary: nil) let destination = MKMapItem(placemark: place) destination.name = "敦南誠品" let items = [destination] let options = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving] MKMapItem.openMapsWithItems(items, launchOptions: options)

Page 30: Standford 2015 week9

模擬器導航

Page 31: Standford 2015 week9

導航:設定起點和終點 var place = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 25.039489, longitude: 121.549328), addressDictionary: nil) let destination = MKMapItem(placemark: place) destination.name = "敦南誠品" place = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 25.033736, longitude: 121.564271), addressDictionary: nil)

let source = MKMapItem(placemark: place) source.name = "台北101" let items = [source, destination]

Page 32: Standford 2015 week9
Page 33: Standford 2015 week9
Page 34: Standford 2015 week9

經緯度轉地址 geoCoder.reverseGeocodeLocation(userLocation.location, completionHandler: { (placemarks:[AnyObject]!, error:NSError!) -> Void in if error == nil && placemarks.count > 0 { let placeMark = placemarks.first as! CLPlacemark for (key, value) in placeMark.addressDictionary { println("\(key) \(value)") } let addressArray = placeMark.addressDictionary["FormattedAddressLines"] as! [String] for address in addressArray { println("\(address)") }

} }) 地址的語⾔言對應iPhone系統設定的語⾔言

Page 35: Standford 2015 week9

地址轉經緯度 geoCoder.geocodeAddressString("臺北市⼤大安區安和路⼀一段", completionHandler: { (placemarks:[AnyObject]!, error:NSError!) -> Void in if error == nil && placemarks.count > 0 { let placeMark = placemarks.first as! CLPlacemark println("\(placeMark.location)") }

})

不⽤用對應iPhone系統設定的語⾔言

Page 36: Standford 2015 week9

注意事項

After initiating a forward-geocoding request, do not attempt to initiate another forward- or reverse-geocoding request.

After initiating a reverse-geocoding request, do not attempt to initiate another reverse- or forward-geocoding request.

Page 37: Standford 2015 week9

地圖標記

利⽤用MKPointAnnotation

let anno = MKPointAnnotation() anno.title = "台北101" anno.subtitle = "shopping的好地⽅方" anno.coordinate = coordinate self.mapView.addAnnotation(anno)

Page 38: Standford 2015 week9

地圖標記class MyAnnotation: NSObject, MKAnnotation { var coordinate:CLLocationCoordinate2D var title:String! var subtitle:String! init(coordinate:CLLocationCoordinate2D, title:String!, subtitle:String!) { self.coordinate = coordinate self.title = title self.subtitle = subtitle super.init()

} }

Page 39: Standford 2015 week9

地圖標記 func addAnnotation() { let coordinate101 = CLLocationCoordinate2D(latitude: 25.033408, longitude: 121.564099) let annotation = MyAnnotation(coordinate: coordinate101, title: "台北101", subtitle: "彼得潘出沒處") mapView.addAnnotation(annotation) }

Page 40: Standford 2015 week9

調整地圖顯⽰示範圍 包含101 & ⺫⽬目前所在位置?

Page 41: Standford 2015 week9

調整地圖顯⽰示範圍

mapSpan.latitudeDelta = ABS(coordinate101.latitude - userLocation.location.coordinate.latitude)*2.1; mapSpan.longitudeDelta = ABS(coordinate101.longitude - userLocation.location.coordinate.longitude)*2.1; region.span = mapSpan;

Page 42: Standford 2015 week9

標記顏⾊色 func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! { var annoView:MKAnnotationView! if annotation is MyAnnotation { annoView = mapView.dequeueReusableAnnotationViewWithIdentifier("Pin") if annoView == nil { annoView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "Pin") (annoView as! MKPinAnnotationView).pinColor = .Green annoView.canShowCallout = true }

} return annoView }

canShowCallout:點選標記顯⽰示內容

Page 43: Standford 2015 week9

區分不同的annotation

在MyAnnotation裡加⼊入屬性區分

Page 44: Standford 2015 week9

標記圖⽚片

annoView.image = UIImage(named: "arrow")

Page 45: Standford 2015 week9

Modal(and Popover) segues

Page 46: Standford 2015 week9

使⽤用者只能和被model出來的controller互動

佔滿畫⾯面(不⼀一定是完全看不到原本controller的畫⾯面,⽐比⽅方alert)

Page 47: Standford 2015 week9
Page 48: Standford 2015 week9

打斷使⽤用者原本的操作,使⽤用上需特別注意

Page 49: Standford 2015 week9

也可以發出notification to communicate result

Page 50: Standford 2015 week9
Page 51: Standford 2015 week9
Page 52: Standford 2015 week9

⻑⾧長按增加地圖標記 @IBAction func addWaypoint(sender: UILongPressGestureRecognizer) { if sender.state == UIGestureRecognizerState.Began { let coordinate = mapView.convertPoint(sender.locationInView(mapView), toCoordinateFromView: mapView) let waypoint = EditableWaypoint(latitude: coordinate.latitude, longitude: coordinate.longitude) waypoint.name = "Dropped" mapView.addAnnotation(waypoint) } }

Page 53: Standford 2015 week9

移動annotationclass EditableWaypoint: GPX.Waypoint { // make coordinate get & set (for draggable annotations) override var coordinate: CLLocationCoordinate2D { get { return super.coordinate } set { latitude = newValue.latitude longitude = newValue.longitude } } }

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! { var view = mapView.dequeueReusableAnnotationViewWithIdentifier(Constants.AnnotationViewReuseIdentifier) if view == nil { view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: Constants.AnnotationViewReuseIdentifier) view.canShowCallout = true } else { view.annotation = annotation } view.draggable = annotation is EditableWaypoint

Page 54: Standford 2015 week9

點選callout的判斷 func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) { if (control as? UIButton)?.buttonType == UIButtonType.DetailDisclosure { mapView.deselectAnnotation(view.annotation, animated: false) performSegueWithIdentifier(Constants.EditWaypointSegue, sender: view) } else if let waypoint = view.annotation as? GPX.Waypoint { if waypoint.imageURL != nil { performSegueWithIdentifier(Constants.ShowImageSegue, sender: view) } } }

Page 55: Standford 2015 week9

notification private var ntfObserver: NSObjectProtocol? private var itfObserver: NSObjectProtocol? private func startObservingTextFields() { let center = NSNotificationCenter.defaultCenter() let queue = NSOperationQueue.mainQueue() ntfObserver = center.addObserverForName(UITextFieldTextDidChangeNotification, object: nameTextField, queue: queue) { notification in if let waypoint = self.waypointToEdit { waypoint.name = self.nameTextField.text } } itfObserver = center.addObserverForName(UITextFieldTextDidChangeNotification, object: infoTextField, queue: queue) { notification in if let waypoint = self.waypointToEdit { waypoint.info = self.infoTextField.text } } } private func stopObservingTextFields() { if let observer = ntfObserver { NSNotificationCenter.defaultCenter().removeObserver(observer) } if let observer = itfObserver { NSNotificationCenter.defaultCenter().removeObserver(observer) } } 如果沒有removeObserver ?

block裡⽤用到self,增加self的retain count ,self不會死掉

Page 56: Standford 2015 week9

利⽤用capture list

center.addObserverForName(UITextFieldTextDidChangeNotification, object: nameTextField, queue: queue) { [weak self] notification in if let waypoint = self?.waypointToEdit { waypoint.name = self?.nameTextField.text } }

Page 57: Standford 2015 week9

 控制iPad上的呈現效果

Page 58: Standford 2015 week9

Popover

Page 59: Standford 2015 week9

控制Popover size

if let ewvc = segue.destinationViewController.contentViewController as? EditWaypointViewController { if let ppc = ewvc.popoverPresentationController { let coordinatePoint = mapView.convertCoordinate(waypoint.coordinate, toPointToView: mapView) ppc.sourceRect = (sender as! MKAnnotationView).popoverSourceRectForCoordinatePoint(coordinatePoint) let minimumSize = ewvc.view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) ewvc.preferredContentSize = CGSize(width: Constants.EditWaypointPopoverWidth, height: minimumSize.height) ppc.delegate = self } ewvc.waypointToEdit = waypoint }

Page 60: Standford 2015 week9

iPhone上的Popover

Page 61: Standford 2015 week9

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle { return UIModalPresentationStyle.OverFullScreen // full screen, but we can see what's underneath }

Page 62: Standford 2015 week9

設定⽑毛玻璃效果 func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? { let navcon = UINavigationController(rootViewController: controller.presentedViewController) let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .ExtraLight)) visualEffectView.frame = navcon.view.bounds navcon.view.insertSubview(visualEffectView, atIndex: 0) // "back-most" subview return navcon }