iOS 平滑移動大頭針視圖(類似滴滴和UBER)

原文發(fā)布于簡書: http://m.itdecent.cn/p/9a51f7a4bfa3
作者: zerojian

大家都知道,地圖上的大頭針視圖如果想平滑的從一個坐標點移動到另一個坐標點,重新添加一個大頭針是無法實現(xiàn)這種效果的,那樣不會有一個移動效果,如果想達到類似滴滴車輛平滑移動效果,就必須在同一個大頭針對象中改變它的坐標,大頭針的視圖方向也要根據(jù)新的角度改變他的視圖角度.

我們這里新建一個 LocationManager 類用來獲取用戶的坐標,當然如果業(yè)務需求是服務器獲取,實現(xiàn)思路類似,只是改變一下坐標點的獲取方式.具體 LocationManager 單例的創(chuàng)建細節(jié)就不多說了,我們在獲取到用戶位置的回調里面添加一個通知

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

主要說說自定義大頭針,大頭針分 MKAnnotationView 用來顯示大頭針視圖, 和 MKAnnotation 用來設置大頭針的一些屬性,可以簡單的把它看成一個 view - model 的關系

如果需要自定義大頭針, 需要遵守 BKAnnotation 協(xié)議,其中 coordinate 屬性是必須實現(xiàn)的屬性

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

在大頭針的初始化方法中,監(jiān)聽定位通知,初始化保存定位的數(shù)組

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

在監(jiān)聽的通知中, 我們保存獲取到的定位對象,為了保證大頭針的移動效果,我們需要在建一個移動的數(shù)組,這個移動數(shù)組保證有一定量的定位數(shù)據(jù)才開始移動動畫效果,而定位數(shù)組只負責把所有獲取到的定位對象保存下來

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

核心的移動大頭針方法,使用遞歸的方式一次次的移動大頭針,在移動數(shù)組全部使用后結束遞歸,等待新獲取到的數(shù)組

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

只能需要注意的幾個細節(jié),一個是大頭針角度,在 CLLocation 對象里面是有一個角度屬性和速度屬性的,當然如果只有兩個坐標點也是可以通過算法獲取到的.

這里要用到的速度和兩個坐標點的距離用來設定車輛移動動畫的時間,這樣可以保證車輛的移動速度是均勻的平滑的.

我們在每次動畫后把創(chuàng)建的 index + 1 ,然后和數(shù)組的 count 通過取余運算獲取到下次遞歸數(shù)組使用的 index

大頭針的平滑移動,只需使用 UIView 動畫簡單的把新的坐標賦值給大頭針即可實現(xiàn).

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

有幫助給個 star 吧!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容