仿链家地图找房_iOS地图找房(类似链家、安居客等地图找房)

题外话:在百度搜索键入:iOS地图找房。你会发现搜索到很多关于这方面的帖子,但是几乎都是询问如何实现的,找不到一个可以研究借鉴的博客。于是我决定补上这个空缺,写的可能不全面,大家体谅。

更新PS:原本我是没打算写Demo出来的,但博客发出来后很多人要,因为网络请求不能发出来,请理解。我把Demo中的网络请求全部干掉了,真正做这个项目的可以加入网络请求,或者花点功夫模拟请求。最后如果觉得有用给个关注或喜欢,谢谢。

先看下美工出的效果图。

地图找房_PxCook.png

下面说说实现的步骤,仍然以代码加注解的方式说明。我尽量说的详尽,其实这个模块难度一般,应该很好理解的,如果有看不懂的给我留言就行了。

分析:第一次进地图要添加很多圆形的大区标识,这时候比例尺应该是整个市区的大小。当点击这个圆形,可以进去小区的房源,这个房源是一个消息框形式的标识,当比例尺在大区,地图移动的时候应该是不允许在更新房源的,当小区的时候,需要更新,而且我们猜测这个更新不能太频繁,可能我们需要设定一个移动距离。同时,大小区的切换,地图放大到某个比例尺切换至小区,地图缩小,切换到大区。

需要做的事情:定义两种标识。添加大区、小区标识。放大缩小后,大小区的判断显示。移动地图大小区的更新。点击大小区不同的响应。

文末我会放上效果GIF。

首先,创建地图,设置比例尺,定位个人位置。比例尺的设定说明下,我这里给了一个自己定义的范围,因为我不希望用户无限放大地图或者无限缩小。最小我希望他看到小区的大小即可,最大差不多展示整个南京市即可。

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];

从效果图中大家能够看出,一共两个大头针样式,一个圆形的,一个是对话框形式。你可以理解为这就是一个大头针,只不过是换了图片而已,那么如何定义自己想要的样式呢?

首先定义一个圆形的大头针,可能需要主标题和副标题

image.png

#import

@interface YLRoundAnnotationView : BMKAnnotationView

@property(nonatomic, strong) NSString *title;

@property(nonatomic, strong) NSString *subTitle;

@end

.m中去实现外观的定义

@interface YLRoundAnnotationView ()

@property(nonatomic, strong) UILabel *titleLabel;

@property(nonatomic, strong) UILabel *subTitleLabel;

@end

@implementation YLRoundAnnotationView

- (id)initWithAnnotation:(id)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设置了圆角,然后在里面添加了两个标题。

下面我们定义第二个大头针,消息框模式的。仍旧仿造上面代码...

image.png

.h

#import

@interface YLMessageAnnotationView : BMKAnnotationView

@property(nonatomic, strong) NSString *title;

@end

.m

@interface YLMessageAnnotationView ()

@property(nonatomic, strong) UIButton *contentView;

@end

@implementation YLMessageAnnotationView

- (id)initWithAnnotation:(id)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 定义完成。我们就可以去网络请求添加大头针了。

如何添加,两种情况:当比例尺很大的时候请求一种大头针,小的时候另一种大头针

- (void)mapView:(BMKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {

NSLog(@"更改了区域");

NSLog(@"当前比例尺%f,过去比例尺:%f",mapView.zoomLevel,self.zoomValue);

// NSLog(@"中心点经纬度 :%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) {

//请求小区

//当没有放大缩小 计算平移的距离。当距离小于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) {//当平移地图。大区不再重复请求

return;

}

//请求大区

[self loadCityAreaHouseWithScale:@"3000" andLatitude:@"" andLongitude:@"" andHouseType:self.houseType andRentType:self.rentType andHouseSize:self.houseSize andMinPrice:self.minPrice andMaxPrice:self.maxPrice];

}

}

在上面这个代理方法中,当比例尺大于14我请求小区的房源。而且我做了个判断,当没有放大缩小 计算平移的距离。当距离小于2千米。不再进行计算 避免过度消耗。当比例尺小于等于14我请求大区的房源。而且当地图平移的时候,不再请求。如何判断地图是否平移和平移后的距离?根据上面if再看下面代码就明白了

- (void)mapView:(BMKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {

self.zoomValue = mapView.zoomLevel;

self.oldCoor = mapView.centerCoordinate;

NSLog(@"之前的比例尺:%f",mapView.zoomLevel);

}

如上,通过地图移动前的中心点经纬度和比例尺去与移动后的做比较即可。

下面看下网络请求的代码

//请求城市区域内的房源组

- (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"]) {//请求大区

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"]) {//请求小区

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来标明到底是大区还是小区。3000代表大区,反之小区。然后解析数据用一个大头针模型YLAnnotationView 来接收。最终把大头针模型加入地图。这时候就会走大头针的数据源方法了。如下:

- (BMKAnnotationView *)mapView:(BMKMapView *)view viewForAnnotation:(id )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;

}

}

在网络请求哪里我给不同区域请求设置了type。这里正好用来判断大头针的显示。这样就做好了区别

最后你可能需要为这个大头针添加点击事件,那么只需要实现这个代理方法

//点击了大头针

- (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;

//取消大头针的选中状态,否则下次再点击同一个则无法响应事件

[mapView deselectAnnotation:annotationView animated:NO];

//计算距离 --> 请求列表数据 --> 完成 --> 展示表格

self.communityId = annotationView.Id;

//计算小区到个人位置的距离

self.distanceText = [NSString stringWithFormat:@"离我:%.1fkm",[self distanceBetweenFromCoor:annotationView.coordinate toCoor:self.locService.userLocation.location.coordinate] / 1000];

[self loadNewListData];

}else {

//点击了区域--->进入小区

//拿到大头针经纬度,放大地图。然后重新计算小区

[mapView setCenterCoordinate:annotationView.coordinate animated:NO];

[mapView setZoomLevel:16];

}

}

在上面我做了一个特殊判断,点击个人位置直接return了。如果不这样可能会程序crash。点击小区我弹出一个房源列表,点击大区,我先移动地图中心点到点击的位置,再把地图放大。注意这个顺序,而且必须不能使用动画。

基本上核心代码就这些了,当然我还做了很多别的功能,例如搜索和检索等...附加功能不再说明。

结语:其实这个功能本身应该是使用百度地图的 高聚合 功能,有兴趣的同学可以去了解这个功能,但是就实际而言,这样重写大头针更好一些。

最后上个效果图吧!

iOS技术交流群:511860085 欢迎加入!

.jpg

Untitled,,d jjj.gif

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值