美文网首页iOS开发部落移动端设计研发iOS开发收集
iOS 平滑移动大头针视图(类似滴滴和UBER)

iOS 平滑移动大头针视图(类似滴滴和UBER)

作者: zerojian | 来源:发表于2016-08-12 22:26 被阅读3635次

原文发布于简书: http://www.jianshu.com/p/9a51f7a4bfa3
作者: zerojian

大家都知道,地图上的大头针视图如果想平滑的从一个坐标点移动到另一个坐标点,重新添加一个大头针是无法实现这种效果的,那样不会有一个移动效果,如果想达到类似滴滴车辆平滑移动效果,就必须在同一个大头针对象中改变它的坐标,大头针的视图方向也要根据新的角度改变他的视图角度.

我们这里新建一个 LocationManager 类用来获取用户的坐标,当然如果业务需求是服务器获取,实现思路类似,只是改变一下坐标点的获取方式.具体 LocationManager 单例的创建细节就不多说了,我们在获取到用户位置的回调里面添加一个通知

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    CLLocation *location = locations.firstObject;
    [[NSNotificationCenter defaultCenter] postNotificationName:kdidUpdateLocation object:location];
}

主要说说自定义大头针,大头针分 MKAnnotationView 用来显示大头针视图, 和 MKAnnotation 用来设置大头针的一些属性,可以简单的把它看成一个 view - model 的关系

如果需要自定义大头针, 需要遵守 BKAnnotation 协议,其中 coordinate 属性是必须实现的属性

@protocol MKAnnotation <NSObject>

// Center latitude and longitude of the annotation view.
// The implementation of this property must be KVO compliant.
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;

@optional

// Title and subtitle for use by selection UI.
@property (nonatomic, readonly, copy, nullable) NSString *title;
@property (nonatomic, readonly, copy, nullable) NSString *subtitle;

// Called as a result of dragging an annotation view.
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate NS_AVAILABLE(10_9, 4_0);

@end

在自定义的大头针里面我们添加几个自定义属性

@property (nonatomic, assign) MyAnnotationType type;

@property (nonatomic, assign) double angle;

@property (nonatomic, strong) MKAnnotationView *annotationView;

其中 type 是一个枚举类型,用来自定义大头针的类型, angle 表示方向,用来显示大头针的方向

在 .m 文件中自定义几个内部属性

    NSMutableArray *locations;
    NSArray *moveArray;
    BOOL movingOver;
    NSInteger currentIndex;
    UIImage *annnationImage;

在大头针的初始化方法中,监听定位通知,初始化保存定位的数组

- (instancetype)initWithType:(MyAnnotationType)type
                  coordinate:(CLLocationCoordinate2D)coordinate
                       angle:(double)angle {

    if (self == [super init]) {

        _type = type;
        _coordinate = coordinate;
        _angle = angle;
        if (type == MyAnnotationTypeCar) {
            [[NSNotificationCenter defaultCenter] addObserver:self                   selector:@selector(onNotifiedCurrentLocationUpdated:)                             name:kdidUpdateLocation
                         object:nil];
            locations = [[NSMutableArray alloc] initWithCapacity:20];
            movingOver = YES;
        }
    }
    return self;
}

在监听的通知中, 我们保存获取到的定位对象,为了保证大头针的移动效果,我们需要在建一个移动的数组,这个移动数组保证有一定量的定位数据才开始移动动画效果,而定位数组只负责把所有获取到的定位对象保存下来

- (void)onNotifiedCurrentLocationUpdated:(NSNotification *)notification
{
    if (notification.object) {
            CLLocation *location = notification.object;
            [locations addObject:location];
            
            if (locations.count >= 5 && movingOver == YES) {
                    moveArray = [NSArray arrayWithArray:locations];
                    [locations removeAllObjects];
                    NSLog(@"----- moveArrayCount: %ld",moveArray.count);
                    NSLog(@"------beging moving -----");
                    currentIndex = 0;
                    movingOver = NO;
                    [self startMoving];
            }
    }
}   

核心的移动大头针方法,使用递归的方式一次次的移动大头针,在移动数组全部使用后结束递归,等待新获取到的数组

- (void)startMoving
{
    NSInteger index = currentIndex % moveArray.count;
    CLLocation *newLocation = moveArray[index];
    
    self.annotationView.image = [annnationImage zj_imageRotatedByAngle:newLocation.course];
    
    CLLocation *currentLocation = [[CLLocation alloc] initWithLatitude:self.coordinate.latitude longitude:self.coordinate.longitude];
    double distance = [newLocation distanceFromLocation:currentLocation];
    
    double speed = newLocation.speed;
    
    CLLocationCoordinate2D newCoordinate = newLocation.coordinate;
    
    [UIView animateWithDuration:distance/speed animations:^{
            self.coordinate = newCoordinate;
            currentIndex ++;
    } completion:^(BOOL finished) {
            if (currentIndex == moveArray.count) {
                    movingOver = YES;
                    moveArray = nil;
            } else {
                    [self startMoving];
            }
    }];
}

只能需要注意的几个细节,一个是大头针角度,在 CLLocation 对象里面是有一个角度属性和速度属性的,当然如果只有两个坐标点也是可以通过算法获取到的.

这里要用到的速度和两个坐标点的距离用来设定车辆移动动画的时间,这样可以保证车辆的移动速度是均匀的平滑的.

我们在每次动画后把创建的 index + 1 ,然后和数组的 count 通过取余运算获取到下次递归数组使用的 index

大头针的平滑移动,只需使用 UIView 动画简单的把新的坐标赋值给大头针即可实现.

项目 DEMO : https://github.com/ZeroJian/SmoothMovingAnnotation

有帮助给个 star 吧!

相关文章

网友评论

  • 丰田李:谢谢分享
  • H5:给一个起点一个终点 能沿路走吗
    H5:应该是小车走过俩个点 在这两点间路径规划 然后拿到这俩点之间的一系列点 进行绘图 是这个思路吗
    H5:有没有一个方法 能获取俩点之间的位置信息 可以让小车绕过公园
    zerojian:@H5 当然可以,你可以测试下
  • H5:我30秒上传一次位置信息 在这段时间内如果小车刚好转弯过了路口 小车不会走这俩点之间的直线
    zerojian:@H5 当然走直线,你30秒上传一次,30秒内的数据并没有
  • H5:如果是市内过十字路口 会不会斜着过去
    zerojian:@dong136279559 是的,因为程序只知道获取的数据,想一直马路走,需要马路数据
    0271fb6f797c:连续两个点的位置在十字路口两边,这个动画效果就是斜着过去的啊?而且车的方向也会有点问题?不是吗?:sweat_smile:
    zerojian:@H5 这个只是记录你的位置,你的位置在哪里就在哪里,不会斜着,因为你的定位在马路上
  • 135893082452:为什么不会动呢?
    zerojian:@135893082452 你不动它当然不动了,获取的定位
    135893082452:@zerojian 我在真机运行,但是那个车怎么都不动
    zerojian:@135893082452 什么?
  • 演员新之助:牛逼 :+1:
    zerojian:@演员新之助 :grin:

本文标题:iOS 平滑移动大头针视图(类似滴滴和UBER)

本文链接:https://www.haomeiwen.com/subject/edyesttx.html