本總結(jié)將第十四和十五課放在了一起,原因有二:第一是略去了ipad開(kāi)發(fā)Demo的部分(因?yàn)楣P者木有ipad,無(wú)法進(jìn)行調(diào)試)。第二是兩節(jié)課都講解了關(guān)于地圖框架的相關(guān)知識(shí),故將二者放在一起總結(jié)贮竟。
在本篇總結(jié)的最后,會(huì)給大家講解在地圖上顯示Flickr上攝影師的照片作品较剃。
Network Activity Indicator
顧名思義咕别,該控件叫做網(wǎng)絡(luò)活動(dòng)指示器。當(dāng)app有網(wǎng)絡(luò)活動(dòng)時(shí)写穴,可以讓狀態(tài)欄左邊的小圓圈滾動(dòng)用來(lái)提示用戶當(dāng)前的網(wǎng)絡(luò)狀態(tài)惰拱。
@property(nonatomic,getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
```
如果設(shè)定為YES,狀態(tài)欄上的小轉(zhuǎn)輪就會(huì)轉(zhuǎn)啊送,反之亦然弓颈。
>注意:應(yīng)用中的所有線程都可使用這個(gè)轉(zhuǎn)輪,我們需要通過(guò)各種方法來(lái)向用戶準(zhǔn)確顯示轉(zhuǎn)輪的狀態(tài)删掀。
#Core Location
-------------
通過(guò)該框架的基本類:```CLLocation```翔冀,我們能獲得設(shè)備處于地球上的位置信息。
##Core Location幾個(gè)重要的屬性:
####1. 坐標(biāo)屬性
```
typedef struct {
CLLocationDegrees latitude; //double value
CLLocationDegrees longitude; //double value
} CLLocationCoordinate2D;
```
####2. 高度
```
@property(readonly, nonatomic) CLLocationDistance altitude; //單位是米
```
####3. 變化精度:
```
@property(readonly, nonatomic) CLLocationAccuracy horizontalAccuracy;//水平精度
@property(readonly, nonatomic) CLLocationAccuracy verticalAccuracy;//高度精度
```
如何獲得CLLocation披泪?
通過(guò)實(shí)例化```CLLocationManager```類纤子,讓其告訴它的代理當(dāng)前設(shè)備所處的位置。
下面來(lái)介紹一下```CLLocationManager```:
# CLLocationManager
-----
## CLLocationManager的工作步驟:
1.查看硬件是否支持位置更新款票。
2.實(shí)例化```CLLocationManager```讓其告訴它的代理當(dāng)前的位置控硼。
3.設(shè)置位置更新的類型(精度)。
```
@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy; //期望的經(jīng)度
@property(assign, nonatomic) CLLocationDistance distanceFilter; //更新到該距離之內(nèi)不要告訴我更新了多少
```
4.開(kāi)始位置監(jiān)控艾少。
```
- (void)startUpdatingLocation;//開(kāi)始更新位置
- (void)stopUpdatingLocation;//停止位置更新
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations;//位置更新的代理方法
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error;//更新失敗
```
##位置監(jiān)控的類型:
####1. 基于精度的監(jiān)控
```
extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation; //最精確卡乾,但是非常耗能
extern const CLLocationAccuracy kCLLocationAccuracyBest;
extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;
extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;
extern const CLLocationAccuracy kCLLocationAccuracyKilometer;
extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;
```
> 注意:精度越高,耗電量越大
####2. 位置發(fā)生重大變化時(shí)更新缚够。
```
- (void)startMonitoringSignificantLocationChanges;
- (void)stopMonitoringSignificantLocationChanges ;
```
該方法在前臺(tái)和后臺(tái)都能監(jiān)控位置的變化幔妨,甚至關(guān)掉app后鹦赎,也可以啟動(dòng)應(yīng)用告訴用戶位置更新:
```
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//如果```launchOptions```存在```UIApplicationLaunchOptionsLocationKey```,說(shuō)明程序啟動(dòng)的原因是因?yàn)槲恢冒l(fā)生了重大變化
return YES;
}
```
####3. 進(jìn)入某個(gè)區(qū)域更新。
3.1設(shè)定一個(gè)圓形的區(qū)域,經(jīng)過(guò)該區(qū)域的時(shí)候會(huì)更新
```
- (void)startMonitoringForRegion:(CLRegion *)region;
- (void)requestStateForRegion:(CLRegion *)region;
```
3.2 通過(guò)一個(gè)信標(biāo)來(lái)監(jiān)控
```
@property (readonly, nonatomic) CLLocationDistance maximumRegionMonitoringDistance;//設(shè)置最大監(jiān)控距離
- (void)startRangingBeaconsInRegion:(CLBeaconRegion *)region;//設(shè)置信標(biāo)
```
####4. 監(jiān)控前進(jìn)的方向
#MapKit
---------
MapKit是用于顯示地圖的框架顶掉,它通過(guò)```MKMapView```來(lái)顯示地圖。
我們來(lái)看一下該框架中幾個(gè)比較重要的元素:
##1. MKMapView
MKMapView就是用來(lái)顯示地圖的View陪踩。
MKMapView的屬性:
```
@property (nonatomic) MKMapType mapType;// MKMapTypeStandard : 標(biāo)準(zhǔn);MKMapTypeSatellite:衛(wèi)星悉抵;MKMapTypeHybrid:疊加
@property (nonatomic) BOOL showsUserLocation; //顯示用戶的地點(diǎn)
@property (nonatomic, readonly, getter=isUserLocationVisible) BOOL userLocationVisible;
//用戶坐標(biāo)是否可見(jiàn)
@property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled; //是否可放大縮小
@property (nonatomic, getter=isRotateEnabled) BOOL rotateEnabled; //是否可旋轉(zhuǎn)
@property (nonatomic, getter=isPitchEnabled) BOOL pitchEnabled; //3D效果
```
##2. MKAnnotationView
在```MKMapView```視圖里肩狂,可以顯示用于標(biāo)注具體位置的“大頭針” ,它是MapKit框架里的```AnnotationView```姥饰。
MKAnnotationView的屬性:
```
@property (nonatomic, strong, nullable) id <MKAnnotation> annotation;
@property (nonatomic, strong, nullable) UIImage *image;//大頭針的圖像
@property (strong, nonatomic, nullable) UIView *leftCalloutAccessoryView;//左附屬對(duì)話框
@property (strong, nonatomic, nullable) UIView *rightCalloutAccessoryView;//右附屬對(duì)話框
@property (nonatomic, getter=isDraggable) BOOL draggable //是否可拖動(dòng)
```
大頭針被點(diǎn)擊時(shí)調(diào)用的方法:
```
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
```
## 3. id<MKAnnotation>
AnnotationView的數(shù)據(jù)源就是:id<MKAnnotation>婚温,任何遵從該協(xié)議的對(duì)象都可以成為AnnotationView的數(shù)據(jù)源,也就是說(shuō)媳否,任何遵守 ```MKAnnotation```協(xié)議的對(duì)象你都可以將其放入地圖中。
我們先看一下在MKMapView里的關(guān)于MKAnnotation的屬性:
```
@property (nonatomic, readonly) NSArray<id<MKAnnotation>> *annotations;//包含MapView所顯示的所有Annotaion
```
注意:annotations是只讀的數(shù)組荆秦,只能添加或者刪除篱竭。
```
- (void)addAnnotation:(id <MKAnnotation>)annotation;
- (void)addAnnotations:(NSArray<id<MKAnnotation>> *)annotations;
- (void)removeAnnotation:(id <MKAnnotation>)annotation;
- (void)removeAnnotations:(NSArray<id<MKAnnotation>> *)annotations;
```
MKAnnotation協(xié)議的方法:
```
@protocol MKAnnotation <NSObject>
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate; //坐標(biāo)
@optional
@property (nonatomic, readonly, copy, nullable) NSString *title;//標(biāo)題
@property (nonatomic, readonly, copy, nullable) NSString *subtitle;//副標(biāo)題
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate ;//設(shè)置坐標(biāo)
```
那么二者是如何關(guān)聯(lián)的呢?
通過(guò)MKMapView的代理方法:
```
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//提供一個(gè) annotation步绸,返回一個(gè) MKAnnotationView
}
```
##3. Callout(對(duì)話框)
點(diǎn)擊大頭針(MKAnnotationView)掺逼,會(huì)出現(xiàn)一個(gè)白底的對(duì)話框,它被叫做```callout```,可以設(shè)置它的主標(biāo)題和副標(biāo)題瓤介。另外還有左右附屬實(shí)圖吕喘,它們可以顯示圖片或者箭頭,也可被點(diǎn)擊刑桑。
#Demo
----
##Demo需求:
- 顯示從flickr抓取的攝影師列表氯质。
- 點(diǎn)擊列表中的一項(xiàng),打開(kāi)地圖祠斧,在當(dāng)前攝影師所照照片的地點(diǎn)顯示大頭針闻察。
- 點(diǎn)擊其中的一個(gè)大頭針,顯示照片詳情:縮略圖和名稱琢锋。
- 點(diǎn)擊箭頭按鈕辕漂,滑入顯示照片的頁(yè)面,顯示原始照片吴超。
##Demo效果圖:
![在地圖顯示照片拍攝位置](http://upload-images.jianshu.io/upload_images/859001-269dd622430d4972.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
##重要代碼段和知識(shí)點(diǎn):
####1. 更改Core Data模型
在上一節(jié)課的基礎(chǔ)上钉嘹,我們需要在模型里的```Photo```實(shí)體添加經(jīng)度和緯度的屬性,還有大頭針縮略圖的URL屬性鲸阻。
在更新屬性后跋涣,一定要重新生成對(duì)應(yīng)該實(shí)體的類文件缨睡,并且要將原app刪除,因?yàn)閿?shù)據(jù)庫(kù)前后是不兼容的仆潮。
![更新模型](http://upload-images.jianshu.io/upload_images/859001-0bf538fa1a08fe3d.gif?imageMogr2/auto-orient/strip)
####2. 新建PhotosByPhotographerMapViewController.h宏蛉,用來(lái)顯示```MKMapView```
因?yàn)橐诘貓D上顯示攝影師所照照片的位置,因此性置,該類的數(shù)據(jù)源來(lái)自攝影師模型:```Photographer```拾并。
```
#import <UIKit/UIKit.h>
#import "Photographer.h"
@interface PhotosByPhotographerMapViewController : UIViewController
@property (nonatomic, strong) Photographer *photographer;//公共API:攝影師
@end
```
```
#import "PhotosByPhotographerMapViewController.h"
#import <MapKit/MapKit.h>
@interface PhotosByPhotographerMapViewController ()<MKMapViewDelegate>
@property (strong, nonatomic) IBOutlet MKMapView *mapView;//地圖view
@property (nonatomic,strong) NSArray *photosByPhotographer;//裝入攝影師擁有的照片的數(shù)組
@end
@implementation PhotosByPhotographerMapViewController
@end
```
####3. 導(dǎo)入Mapkit的framework
需要注意的是,除了要在類文件引用```<MapKit/MapKit.h>```框架以外鹏浅,還要手動(dòng)向項(xiàng)目中添加該框架:
![手動(dòng)添加MapKit框架.gif](http://upload-images.jianshu.io/upload_images/859001-8fb04dfbac7bbdd7.gif?imageMogr2/auto-orient/strip)
####4. 更新photographer和mapView后更新annotation:
```
- (void)setMapView:(MKMapView *)mapView
{
_mapView = mapView;
//設(shè)置代理
self.mapView.delegate = self;
//更新
[self updateMapViewAnnotations];
}
- (void)setPhotographer:(Photographer *)photographer
{
_photographer = photographer;
//導(dǎo)航欄標(biāo)題
self.title = photographer.name;
//準(zhǔn)備更新數(shù)組嗅义,要事先設(shè)置其為nil,否則不會(huì)生成新的
self.photosByPhotographer = nil;
[self updateMapViewAnnotations];
}
- (void)updateMapViewAnnotations
{
[self.mapView removeAnnotations:self.mapView.annotations];
[self.mapView addAnnotations:self.photosByPhotographer];
[self.mapView showAnnotations:self.photosByPhotographer animated:YES];
}
```
####5. 自定義點(diǎn)擊大頭針后顯示的view
```
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//類似UITableviewCell的復(fù)用
static NSString *reuseId = @"PhotosByPhotographerMapViewController";
MKPinAnnotationView *view = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (!view) {
view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
//是否顯示callout
view.canShowCallout = YES;
//設(shè)置左部分的callout:UIImageView
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 46, 46)];
view.leftCalloutAccessoryView = imageView;
//設(shè)置右部分的callout:UIButton
UIButton *disclosurebutton = [[UIButton alloc] init];
[disclosurebutton setBackgroundImage:[UIImage imageNamed:@"disclosure"] forState:UIControlStateNormal];
[disclosurebutton sizeToFit];
view.rightCalloutAccessoryView = disclosurebutton;
}
view.annotation = annotation;
return view;
}
```
####6. 點(diǎn)擊大頭針隐砸,更新callout左側(cè)顯示的縮略圖
```
/**
* 點(diǎn)擊大頭針view
*
* @param mapView 大頭針?biāo)鶎俚膍apView
* @param view 大頭針view
*/
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
[self updateLeftCalloutAccessoryViewInAnnotationView:view];
}
/**
* 更新callout里的圖片(在左側(cè))
*
* @param annotationView 當(dāng)前被點(diǎn)擊的大頭針view
*/
- (void)updateLeftCalloutAccessoryViewInAnnotationView:(MKAnnotationView *)annotationView
{
UIImageView *imageView = nil;
if ([annotationView.leftCalloutAccessoryView isKindOfClass:[UIImageView class]]) {
imageView = (UIImageView *)annotationView.leftCalloutAccessoryView;
}
if (imageView) {
Photo *photo = nil;
if ([annotationView.annotation isKindOfClass:[Photo class]]) {
photo = (Photo *)annotationView.annotation;
}
if (photo) {
NSString *urlString = photo.thumbnailURL;
imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]]];
}
}
}
```
>注意:顯示圖片的代碼:``` imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]]];```方法會(huì)阻塞主線程之碗,實(shí)際操作中應(yīng)該放在子線程中執(zhí)行。詳情請(qǐng)參考筆者另一篇講解關(guān)于多線程的博客:[最淺顯易懂的iOS多線程技術(shù) - GCD的教程](http://www.reibang.com/p/6e74f5438f2c)季希。
####7. 點(diǎn)擊callout褪那,在下一頁(yè)面顯示原圖
```
/**
* 點(diǎn)擊callout實(shí)行跳轉(zhuǎn)
*
* @param mapView 當(dāng)前的mapView
* @param view 當(dāng)前callout所屬的AnnotationView
* @param control callout內(nèi)部被點(diǎn)擊的控件
*/
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:@"Show Photo" sender:view];
}
/**
* 調(diào)轉(zhuǎn)執(zhí)行前的代碼
*
* @param segue 連接前后兩個(gè)控制器的segue
* @param sender 被點(diǎn)擊的AnnotaionView
*/
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([sender isKindOfClass:[MKAnnotationView class]]) {
[self prepareViewController:segue.destinationViewController
forSegue:segue.identifier
toShowAnnotation:((MKAnnotationView *)sender).annotation];
}
}
/**
* 為目標(biāo)控制器準(zhǔn)備數(shù)據(jù)(圖片的URL)
*
* @param vc 目標(biāo)控制器
* @param segueIdentifier segue.identifier
* @param annotation 被點(diǎn)擊的AnnotaionView
*/
- (void)prepareViewController:(id)vc
forSegue:(NSString *)segueIdentifier
toShowAnnotation:(id <MKAnnotation>)annotation
{
Photo *photo = nil;
if ([annotation isKindOfClass:[Photo class]]) {
photo = (Photo *)annotation;
}
if (photo) {
if (![segueIdentifier length] || [segueIdentifier isEqualToString:@"Show Photo"]) {
if ([vc isKindOfClass:[ImageViewController class]]) {
ImageViewController *ivc = (ImageViewController *)vc;
ivc.imageURL = [NSURL URLWithString:photo.imageURL];
ivc.title = photo.title;
}
}
}
}
```
>注意:這里的``` ivc.imageURL = [NSURL URLWithString:photo.imageURL];```代碼同樣會(huì)阻塞主線程,實(shí)際操作中應(yīng)該放在子線程來(lái)做式塌!
>而且,本demo的圖片地址應(yīng)該都是在墻外的博敬,所以最好先讓電腦翻墻,然后在模擬器上運(yùn)行比較好峰尝。
#最后的話
----
如果哪位小伙伴想拿到本文Demo的代碼請(qǐng)不要客氣偏窝,可以進(jìn)入我的[GitHub](https://github.com/Shijie0111/Stanford_iOS_Lecture_DemoBundle)下載哦~ 這一系列到現(xiàn)在為止的所有Demo都在里面,分為英文注釋版本和中文注釋版本兩種武学。
十分歡迎給筆者的代碼和文筆拋出寶貴的意見(jiàn)和建議~
本文已在版權(quán)印備案祭往,如需轉(zhuǎn)載請(qǐng)?jiān)L問(wèn)版權(quán)印。48422928
[獲取授權(quán)](http://101702070004705.bqy.pub)
#### -------------------------------- 2018年7月17日更新 --------------------------------
**注意注意;鹬稀E鸩埂!**
筆者在近期開(kāi)通了個(gè)人公眾號(hào)熏矿,主要分享編程括勺,讀書(shū)筆記,思考類的文章曲掰。
- **編程類**文章:包括筆者以前發(fā)布的精選技術(shù)文章疾捍,以及后續(xù)發(fā)布的技術(shù)文章(以原創(chuàng)為主),并且逐漸脫離 iOS 的內(nèi)容栏妖,將側(cè)重點(diǎn)會(huì)轉(zhuǎn)移到**提高編程能力**的方向上乱豆。
- **讀書(shū)筆記類**文章:分享**編程類**,**思考類**吊趾,**心理類**宛裕,**職場(chǎng)類**書(shū)籍的讀書(shū)筆記瑟啃。
- **思考類**文章:分享筆者平時(shí)在**技術(shù)上**,**生活上**的思考揩尸。
>因?yàn)楣娞?hào)每天發(fā)布的消息數(shù)有限制蛹屿,所以到目前為止還沒(méi)有將所有過(guò)去的精選文章都發(fā)布在公眾號(hào)上,后續(xù)會(huì)逐步發(fā)布的岩榆。
**而且因?yàn)楦鞔蟛┛推脚_(tái)的各種限制错负,后面還會(huì)在公眾號(hào)上發(fā)布一些短小精干,以小見(jiàn)大的干貨文章哦~**
掃下方的公眾號(hào)二維碼并點(diǎn)擊關(guān)注勇边,期待與您的共同成長(zhǎng)~
![公眾號(hào):程序員維他命](http://upload-images.jianshu.io/upload_images/859001-5bddfacafb9e9079.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)