題外話:在百度搜索鍵入:iOS地圖找房泳梆。你會發(fā)現(xiàn)搜索到很多關于這方面的帖子鳖悠,但是幾乎都是詢問如何實現(xiàn)的,找不到一個可以研究借鑒的博客优妙。于是我決定補上這個空缺竞穷,寫的可能不全面,大家體諒鳞溉。
更新PS:原本我是沒打算寫Demo出來的瘾带,但博客發(fā)出來后很多人要,因為網(wǎng)絡請求不能發(fā)出來熟菲,請理解看政。我把Demo
中的網(wǎng)絡請求全部干掉了,真正做這個項目的可以加入網(wǎng)絡請求抄罕,或者花點功夫模擬請求允蚣。最后如果覺得有用給個關注或喜歡,謝謝呆贿。
先看下美工出的效果圖嚷兔。
下面說說實現(xiàn)的步驟森渐,仍然以代碼加注解的方式說明。我盡量說的詳盡冒晰,其實這個模塊難度一般同衣,應該很好理解的,如果有看不懂的給我留言就行了壶运。
分析:第一次進地圖要添加很多圓形的大區(qū)標識耐齐,這時候比例尺應該是整個市區(qū)的大小。當點擊這個圓形蒋情,可以進去小區(qū)的房源埠况,這個房源是一個消息框形式的標識,當比例尺在大區(qū)棵癣,地圖移動的時候應該是不允許在更新房源的辕翰,當小區(qū)的時候,需要更新狈谊,而且我們猜測這個更新不能太頻繁喜命,可能我們需要設定一個移動距離。同時的畴,大小區(qū)的切換渊抄,地圖放大到某個比例尺切換至小區(qū)尝胆,地圖縮小丧裁,切換到大區(qū)。
需要做的事情:定義兩種標識含衔。添加大區(qū)煎娇、小區(qū)標識。放大縮小后贪染,大小區(qū)的判斷顯示缓呛。移動地圖大小區(qū)的更新。點擊大小區(qū)不同的響應杭隙。
文末我會放上效果GIF哟绊。
首先,創(chuàng)建地圖痰憎,設置比例尺票髓,定位個人位置。比例尺的設定說明下铣耘,我這里給了一個自己定義的范圍洽沟,因為我不希望用戶無限放大地圖或者無限縮小。最小我希望他看到小區(qū)的大小即可蜗细,最大差不多展示整個南京市即可裆操。
self.mapView = [[BMKMapView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
[self.view addSubview:self.mapView];
self.locService = [[BMKLocationService alloc] init];
self.mapView.delegate = self;
self.locService.delegate = self;
self.mapView.showsUserLocation = YES;
self.mapView.showMapScaleBar = YES;//顯示比例尺
self.mapView.mapScaleBarPosition = CGPointMake(10, 75);//比例尺位置
self.mapView.minZoomLevel = 11;
self.mapView.maxZoomLevel = 17;
self.mapView.userTrackingMode = BMKUserTrackingModeNone;
[self.locService startUserLocationService];
從效果圖中大家能夠看出怒详,一共兩個大頭針樣式,一個圓形的踪区,一個是對話框形式昆烁。你可以理解為這就是一個大頭針,只不過是換了圖片而已朽缴,那么如何定義自己想要的樣式呢善玫?
首先定義一個圓形的大頭針,可能需要主標題和副標題
#import <BaiduMapAPI_Map/BMKMapComponent.h>
@interface YLRoundAnnotationView : BMKAnnotationView
@property(nonatomic, strong) NSString *title;
@property(nonatomic, strong) NSString *subTitle;
@end
.m中去實現(xiàn)外觀的定義
@interface YLRoundAnnotationView ()
@property(nonatomic, strong) UILabel *titleLabel;
@property(nonatomic, strong) UILabel *subTitleLabel;
@end
@implementation YLRoundAnnotationView
- (id)initWithAnnotation:(id<BMKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]) {
[self setBounds:CGRectMake(0.f, 0.f, 80, 80)];
[self setContentView];
}
return self;
}
- (void)setContentView {
UIColor *color = [UIColor colorWithRed:234/255. green:130/255. blue:80/255. alpha:1];
self.layer.cornerRadius = 40;
self.layer.borderColor = color.CGColor;
self.layer.borderWidth = 1;
self.layer.masksToBounds = YES;
self.backgroundColor = color;
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame)/2.5)];
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.titleLabel.font = font(15);
self.titleLabel.textColor = [UIColor whiteColor];
self.titleLabel.layer.masksToBounds = YES;
[self addSubview:self.titleLabel];
self.subTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.titleLabel.frame), CGRectGetWidth(self.frame), CGRectGetHeight(self.frame)/3)];
self.subTitleLabel.textAlignment = NSTextAlignmentCenter;
self.subTitleLabel.font = font(13);
self.subTitleLabel.textColor = [UIColor whiteColor];
self.subTitleLabel.layer.masksToBounds = YES;
[self addSubview:self.subTitleLabel];
}
- (void)setTitle:(NSString *)title {
_title = title;
self.titleLabel.text = title;
}
- (void)setSubTitle:(NSString *)subTitle {
_subTitle = subTitle;
self.subTitleLabel.text = subTitle;
}
上面我們重寫了大頭針的bound
設置了圓角密强,然后在里面添加了兩個標題茅郎。
下面我們定義第二個大頭針,消息框模式的或渤。仍舊仿造上面代碼...
.h
#import <BaiduMapAPI_Map/BMKMapComponent.h>
@interface YLMessageAnnotationView : BMKAnnotationView
@property(nonatomic, strong) NSString *title;
@end
.m
@interface YLMessageAnnotationView ()
@property(nonatomic, strong) UIButton *contentView;
@end
@implementation YLMessageAnnotationView
- (id)initWithAnnotation:(id<BMKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]) {
[self setBounds:CGRectMake(0.f, 0.f, 80, 30)];
[self setContentView];
}
return self;
}
- (void)setContentView {
self.contentView = [UIButton buttonWithType:UIButtonTypeCustom];
self.contentView.frame = self.bounds;
self.contentView.userInteractionEnabled = NO;
self.contentView.titleLabel.textAlignment = NSTextAlignmentCenter;
[self.contentView setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.contentView setBackgroundImage:[UIImage imageNamed:@"community"] forState:UIControlStateNormal];
self.contentView.titleEdgeInsets = UIEdgeInsetsMake(-5, 0, 0, 0);
self.contentView.titleLabel.font = font(10);
[self addSubview:self.contentView];
}
- (void)setTitle:(NSString *)title {
_title = title;
[self.contentView setTitle:title forState:UIControlStateNormal];
}
為什么放一個Button
系冗,因為方便標題和背景設置...
ok 定義完成械荷。我們就可以去網(wǎng)絡請求添加大頭針了浸踩。
如何添加,兩種情況:當比例尺很大的時候請求一種大頭針蠢熄,小的時候另一種大頭針
- (void)mapView:(BMKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
NSLog(@"更改了區(qū)域");
NSLog(@"當前比例尺%f池磁,過去比例尺:%f",mapView.zoomLevel,self.zoomValue);
// NSLog(@"中心點經(jīng)緯度 :%f,%f",mapView.centerCoordinate.latitude,mapView.centerCoordinate.longitude);
if (mapView.zoomLevel > self.zoomValue) {
NSLog(@"地圖放大了");
}else if (mapView.zoomLevel < self.zoomValue){
NSLog(@"地圖縮小了");
}
if (mapView.zoomLevel > 14) {
//請求小區(qū)
//當沒有放大縮小 計算平移的距離奔害。當距離小于2千米。不再進行計算 避免過度消耗
float distance = [self distanceBetweenFromCoor:self.oldCoor toCoor:mapView.centerCoordinate];
if (distance <= 1000 && mapView.zoomLevel == self.zoomValue) {
return;
}
[self loadCityAreaHouseWithScale:@"1000" andLatitude:[NSString stringWithFormat:@"%f",mapView.centerCoordinate.latitude] andLongitude:[NSString stringWithFormat:@"%f",mapView.centerCoordinate.longitude] andHouseType:self.houseType andRentType:self.rentType andHouseSize:self.houseSize andMinPrice:self.minPrice andMaxPrice:self.maxPrice];
}else if(mapView.zoomLevel <= 14) {
if (mapView.zoomLevel == self.zoomValue) {//當平移地圖地熄。大區(qū)不再重復請求
return;
}
//請求大區(qū)
[self loadCityAreaHouseWithScale:@"3000" andLatitude:@"" andLongitude:@"" andHouseType:self.houseType andRentType:self.rentType andHouseSize:self.houseSize andMinPrice:self.minPrice andMaxPrice:self.maxPrice];
}
}
在上面這個代理方法中华临,當比例尺大于14我請求小區(qū)的房源。而且我做了個判斷端考,當沒有放大縮小 計算平移的距離雅潭。當距離小于2千米。不再進行計算 避免過度消耗却特。當比例尺小于等于14我請求大區(qū)的房源扶供。而且當?shù)貓D平移的時候,不再請求裂明。如何判斷地圖是否平移和平移后的距離椿浓?根據(jù)上面if再看下面代碼就明白了
- (void)mapView:(BMKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
self.zoomValue = mapView.zoomLevel;
self.oldCoor = mapView.centerCoordinate;
NSLog(@"之前的比例尺:%f",mapView.zoomLevel);
}
如上,通過地圖移動前的中心點經(jīng)緯度和比例尺去與移動后的做比較即可闽晦。
下面看下網(wǎng)絡請求的代碼
//請求城市區(qū)域內的房源組
- (void)loadCityAreaHouseWithScale:(NSString *)scale andLatitude:(NSString *)latitude andLongitude:(NSString *)longitude andHouseType:(NSString *)houseType andRentType:(NSString *)rentType andHouseSize:(NSString *)houseSize andMinPrice:(NSString *)minPrice andMaxPrice:(NSString *)maxPrice {
WeakSelf
[SVProgressHUD show];
[MapFindHouseViewModel mapFindHouseWithLatitude:latitude andLongitude:longitude andScale:scale andHouseType:houseType andRentType:rentType andHouseSize:houseSize andMinPrice:minPrice andMaxPrice:maxPrice andBlock:^(id result) {
NSArray *data = result;
if (data.count > 0) {
[weakSelf.mapView removeAnnotations:weakSelf.mapView.annotations];
if ([scale isEqualToString:@"3000"]) {//請求大區(qū)
for (NSDictionary *dic in data) {
YLAnnotationView *an = [[YLAnnotationView alloc] init];
CLLocationCoordinate2D coor;
coor.latitude = [dic[@"lat"] floatValue];
coor.longitude = [dic[@"lng"] floatValue];
an.type = 1;
an.coordinate = coor;
an.title = dic[@"description"];
an.subtitle = [NSString stringWithFormat:@"%@套",dic[@"houses"]];
an.Id = dic[@"id"];
[weakSelf.mapView addAnnotation:an];
}
}else if([scale isEqualToString:@"1000"]) {//請求小區(qū)
for (NSDictionary *dic in data) {
YLAnnotationView *an = [[YLAnnotationView alloc] init];
CLLocationCoordinate2D coor;
coor.latitude = [dic[@"lat"] floatValue];
coor.longitude = [dic[@"lng"] floatValue];
an.type = 2;
an.coordinate = coor;
an.title = [NSString stringWithFormat:@"%@ | %@套",dic[@"description"],dic[@"houses"]];
an.Id = dic[@"id"];
[weakSelf.mapView addAnnotation:an];
}
}
}else {
[SVProgressHUD showInfoWithStatus:@"無房源!請更改條件~"];
}
}];
}
前面我傳進來一個scale
來標明到底是大區(qū)還是小區(qū)扳碍。3000代表大區(qū),反之小區(qū)尼荆。然后解析數(shù)據(jù)用一個大頭針模型YLAnnotationView
來接收左腔。最終把大頭針模型加入地圖。這時候就會走大頭針的數(shù)據(jù)源方法了捅儒。如下:
- (BMKAnnotationView *)mapView:(BMKMapView *)view viewForAnnotation:(id <BMKAnnotation>)annotation {
// 生成重用標示identifier
YLAnnotationView *anno = (YLAnnotationView *)annotation;
if (anno.type == 1) {
NSString *AnnotationViewID = @"round";
// 檢查是否有重用的緩存
YLRoundAnnotationView *annotationView = (YLRoundAnnotationView *)[view dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
// 緩存沒有命中液样,自己構造一個振亮,一般首次添加annotation代碼會運行到此處
if (annotationView == nil) {
annotationView = [[YLRoundAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID];
annotationView.paopaoView = nil;
}
// 設置偏移位置
annotationView.centerOffset = CGPointMake(0, -(annotationView.frame.size.height * 0.5));
annotationView.title = anno.title;
annotationView.subTitle = anno.subtitle;
annotationView.annotation = anno;
annotationView.canShowCallout = NO;
return annotationView;
}else {
NSString *AnnotationViewID = @"message";
// 檢查是否有重用的緩存
YLMessageAnnotationView *annotationView = (YLMessageAnnotationView *)[view dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
// 緩存沒有命中,自己構造一個鞭莽,一般首次添加annotation代碼會運行到此處
if (annotationView == nil) {
annotationView = [[YLMessageAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID];
annotationView.paopaoView = nil;
}
// 設置偏移位置
annotationView.centerOffset = CGPointMake(0, -(annotationView.frame.size.height * 0.5));
annotationView.title = anno.title;
annotationView.annotation = anno;
annotationView.canShowCallout = NO;
return annotationView;
}
}
在網(wǎng)絡請求哪里我給不同區(qū)域請求設置了type
坊秸。這里正好用來判斷大頭針的顯示。這樣就做好了區(qū)別
最后你可能需要為這個大頭針添加點擊事件澎怒,那么只需要實現(xiàn)這個代理方法
//點擊了大頭針
- (void)mapView:(BMKMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view {
if (view.annotation.coordinate.latitude == self.locService.userLocation.location.coordinate.latitude) {//個人位置特殊處理褒搔,否則類型不匹配崩潰
NSLog(@"點擊了個人位置");
return;
}
YLAnnotationView *annotationView = (YLAnnotationView *)view.annotation;
if (annotationView.type == 2) {
self.areaTitle = annotationView.title;
//取消大頭針的選中狀態(tài),否則下次再點擊同一個則無法響應事件
[mapView deselectAnnotation:annotationView animated:NO];
//計算距離 --> 請求列表數(shù)據(jù) --> 完成 --> 展示表格
self.communityId = annotationView.Id;
//計算小區(qū)到個人位置的距離
self.distanceText = [NSString stringWithFormat:@"離我:%.1fkm",[self distanceBetweenFromCoor:annotationView.coordinate toCoor:self.locService.userLocation.location.coordinate] / 1000];
[self loadNewListData];
}else {
//點擊了區(qū)域--->進入小區(qū)
//拿到大頭針經(jīng)緯度喷面,放大地圖星瘾。然后重新計算小區(qū)
[mapView setCenterCoordinate:annotationView.coordinate animated:NO];
[mapView setZoomLevel:16];
}
}
在上面我做了一個特殊判斷,點擊個人位置直接return
了惧辈。如果不這樣可能會程序crash
琳状。點擊小區(qū)我彈出一個房源列表,點擊大區(qū)盒齿,我先移動地圖中心點到點擊的位置念逞,再把地圖放大。注意這個順序边翁,而且必須不能使用動畫翎承。
基本上核心代碼就這些了,當然我還做了很多別的功能符匾,例如搜索和檢索等...附加功能不再說明叨咖。
結語:其實這個功能本身應該是使用百度地圖的 高聚合 功能,有興趣的同學可以去了解這個功能待讳,但是就實際而言芒澜,這樣重寫大頭針更好一些仰剿。
最后上個效果圖吧!
iOS技術交流群:511860085
歡迎加入创淡!