2014-01-30 4 views
2

Конечное состояние, которое я пытаюсь достичь, - это запустить таймер, который обновляет коллекцию «Автомобиль» annotations. annotationcoordinates успешно обновляется каждые 60 секунд с использованием таймера, но пользователь должен вызвать mapView: regionWillChangeAnimated и mapView: regionWillChangeAnimated делегатов. Эти делегаты правильно работают и перемещают Транспортное средство annotations, но я хочу, чтобы annotations двигался автономно, без взаимодействия с экраном.MapKit - обновление MapView для отображения Перемещение аннотаций с помощью таймера

Вот мой подход:

1) Запустить таймер .. это работает отлично!

//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 
#pragma mark Timers 
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 
dispatch_source_t CreateDispatchTimer(uint64_t interval, 
            uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block) 
{ 
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 
if (timer) 
{ 
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, interval), interval * NSEC_PER_SEC, (1ull * NSEC_PER_SEC)/10); 
    dispatch_source_set_event_handler(timer, block); 
    dispatch_resume(timer); 
} 
return timer; 
} 

- (void)startTimer 
{ 
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
double secondsToFire = 60.000f; 
double secondsLeeway = 2.000f; 

_timer = CreateDispatchTimer(secondsToFire, secondsLeeway, queue, ^{ 
    // Do something 
    //Method Call to Refresh NSMutableArray of Vehicle Models 
    [self RefreshVehicles]; 
    NSLog(@"TIMER TASK CALLED"); 
}); 
} 

- (void)cancelTimer 
{ 
if (_timer) { 
    dispatch_source_cancel(_timer);   
    _timer = nil; 
    } 
} 

Таймер используется для извлечения и загрузки последних кораблей в NSMutable Array по телефону (void)RefreshVehicles, это будет обновлять последнюю coordinates для каждого объекта, который будет использоваться для обновления транспортного средства annotation. Я использую NSNotification, чтобы узнать, когда работа в сети Async Network Call и SQLite завершена, чтобы обновить записи транспортного средства. Когда уведомление срабатывает, Я удаляю все существующие Vehicle annotations, а затем обновить локальную Vehicle NSMutable Array, позвонив по телефону addVehiclesToMap добавить новый annotations к карте:

//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 
#pragma mark Notification Listener 
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 

- (void)RefreshVehicles:(NSNotification *)notif { 

    NSLog(@"++++++++++++++++++++++++++++++++++++++++ Vehicles UPDATED!"); 

** POST SOLUTION REMARK: MOVED REMOVE ANNOTATION LOGIC TO: (void)addVehiclesToMap 
//** MOVED //If it already Exists, Remove it, Must redraw vehicles because they are moving. 
//** MOVED for (id <MKAnnotation> annotation in self.mapView.annotations) 
//** MOVED{ 
    //** MOVED//Only Remove Vehicles, Leave Stations, they are static 
    //** MOVED if ([annotation isKindOfClass:[VehicleAnnotation class]]) 
    //** MOVED{ 
     //** MOVED [self.mapView removeAnnotation:annotation]; 
    //** MOVED} 
//** MOVED} 

//Load Vehicle Collection 
self.vehicleCollection = [[VehicleModelDataController defaultVehicleModelDataController] vehicleReturnAll];  

[self addVehiclesToMap]; 

} 

Вот метод для addVehiclesToMap: ** POST рЕШЕНИЕ Замечание: После внедрения решения Анны обновить карту annotations на Main Thread, я начал получать следующее сообщение об ошибке: *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x16d94af0> was mutated while being enumerated. '

Это потому, что я удалял аннотации от обновления таймера в фоновом потоке. Чтобы устранить эту проблему, я осуществил [self.mapView removeAnnotation:annotation]; к главному потоку **

/* 
----- VEHICLES ----- 
*/ 
- (void)addVehiclesToMap { 

//If it already Exists, Remove it, Must redraw vehicles because they are moving. 
for (id <MKAnnotation> annotation in self.mapView.annotations) 
{ 
    //Only Remove Vehicles, Leave Stations, they are static 
    if ([annotation isKindOfClass:[VehicleAnnotation class]]) 
    { 
     //Remove Vehicle Annotation to MapView on the Main Thread 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [self.mapView removeAnnotation:annotation]; 
     }); 
    } 
} 

//Loop through Vehicle Collection and generate annotation for each Vehicle Object 
for (VehicleModel *vehicle in vehicleCollection) { 

    //New Vehicle Annotation Instance 
    VehicleAnnotation *myVehicleAnnotation = [[VehicleAnnotation alloc] init]; 

    myVehicleAnnotation.coordinate = CLLocationCoordinate2DMake([vehicle.vehicleLat doubleValue], [vehicle.vehicleLon doubleValue]); 
    myVehicleAnnotation.vehicleId = [vehicle.vehicleId stringValue];     
    myVehicleAnnotation.title = vehicle.vehicleLabel;        
    myVehicleAnnotation.subtitle = vehicle.vehicleIsTrainDelayed;     

    **POST SOLUTION REMARK: PER ANNA'S SOLUTION, MOVE addAnnodation TO MAIN THREAD:** 
    //MODIFIED THIS:** [self.mapView addAnnotation:myVehicleAnnotation]; 

    **//TO THIS:** 
    //Add Vehicle Annotation to MapView on the Main Thread 
    dispatch_async(dispatch_get_main_queue(), ^{ 

     [self.mapView addAnnotation:myVehicleAnnotation]; 
    });** 
} 

}

Затем код для viewAnnotation делегата:.

//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 
#pragma mark MKAnnotationView Delegate 
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 


- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id  <MKAnnotation>)annotation 
{ 
    // if it's the user location, just return nil. 
    if ([annotation isKindOfClass:[MKUserLocation class]]) 
    return nil; 


// handle our two custom annotations 
// 
if ([annotation isKindOfClass:[VehicleAnnotation class]]) /// for Vehicles Only 
{ 

    //Important, can't use annotation, this lets the compiler know that the annotation is actually an StationAnnotation object. 
    VehicleAnnotation *vehicleAnnotation = (VehicleAnnotation *)annotation; 

    //Reuse existing Annotation 
    NSString* AnnotationIdentifier = vehicleAnnotation.vehicleId.lowercaseString; 
    MKPinAnnotationView* pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier]; 

    if (!pinView) 
    { 

     //Set unique annotation identifier exp: 304 (The Vehicles's Unique Number) 
     NSString* AnnotationIdentifier = vehicleAnnotation.vehicleId.lowercaseString; 


     MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier]; 
     annotationView.canShowCallout = YES; 


     NSString *vehicleFlagIcon = [@"map_train_green_" stringByAppendingString:vehicleAnnotation.vehicleId.lowercaseString]; 
     UIImage *flagImage = [UIImage imageNamed:vehicleFlagIcon]; 

     CGRect resizeRect; 

     resizeRect.size = flagImage.size; 
     CGSize maxSize = CGRectInset(self.view.bounds, 
            [VehicleMapViewController annotationPadding], 
            [VehicleMapViewController calloutHeight]).size; 

     maxSize.height -= self.navigationController.navigationBar.frame.size.height + [VehicleMapViewController calloutHeight]; 
     if (resizeRect.size.width > maxSize.width) 
      resizeRect.size = CGSizeMake(maxSize.width, resizeRect.size.height/resizeRect.size.width * maxSize.width); 
     if (resizeRect.size.height > maxSize.height) 
      resizeRect.size = CGSizeMake(resizeRect.size.width/resizeRect.size.height * maxSize.height, maxSize.height); 

     resizeRect.origin = (CGPoint){0.0f, 0.0f}; 
     UIGraphicsBeginImageContext(resizeRect.size); 
     [flagImage drawInRect:resizeRect]; 
     UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext(); 
     UIGraphicsEndImageContext(); 

     annotationView.image = resizedImage; 
     annotationView.opaque = NO; 

     NSString *vehicleLogo = [@"map_train_green_" stringByAppendingString:vehicleAnnotation.vehicleId.lowercaseString]; 
     UIImageView *sfIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:vehicleLogo]]; 
     annotationView.leftCalloutAccessoryView = sfIconView; 

     return annotationView; 

    } 
    else 
    { 
     pinView.annotation = annotation; 
    } 
    return pinView; 
}  

return nil; 
} 

Следующая У меня есть логика, чтобы удалить затем повторно добавьте annotations, используя следующие делегаты. Эти делегаты работают очень хорошо, но требуют от пользователя взаимодействия с экраном. Я пытаюсь обновить карту каждые X секунд. До сих пор, чтобы увидеть какие-либо изменения в местоположениях annotation, я должен коснуться экрана и переместить карту, чтобы вызвать эти удаления. Я все еще не могу смотреть, как автомобили движутся автономно, не требуя взаимодействия.

POST РЕШЕНИЯ СЛОВА: Я устранил эти ДЕЛЕГАТ, потому что они ВЫПОЛНЕНИЕ ООН необходимых изменений АВТОМОБИЛЯ АННОТАЦИИ СОЗДАНИЯ значков фликера когда карта была тронута и Перенесена ... Позволить ТАЙМЕР ДЕЛАТЬ РАБОТУ

**// DELETED** -(void)mapView:(MKMapView *)theMapView regionWillChangeAnimated:(BOOL)animated { ...Annotation tear down and rebuild code here } 
**//DELETED** -(void)mapView:(MKMapView *)theMapView didUpdateUserLocation:(MKUserLocation *)userLocation { ...Annotation tear down and rebuild code here } 

Я даже близко к решению здесь? Заранее спасибо...

ответ

4

Попробуйте называть addAnnotation на главном потоке, поэтому обновления пользовательского интерфейса без этой задержки:

dispatch_async(dispatch_get_main_queue(), ^{ 
    [self.mapView addAnnotation:myVehicleAnnotation]; 
}); 



неродственного предложение:
Вместо удаления и повторного добавления аннотаций автомобиля , вы можете просто обновить свойства существующих транспортных средств coordinate, и на экране карты автоматически будут перемещаться аннотации. Это может привести к слегка более плавному эффекту, хотя немного сложнее реализовать логику (например, вам нужно будет найти существующую аннотацию транспортного средства в виде карты annotations и обновить ее вместо создания нового VehicleAnnotation). Вы также должны учитывать новые транспортные средства и удалять аннотации для автомобилей, которые больше не существуют.


Другой несвязанный предложение:
Вместо этого кольцевой и загадочным образом установить координату:

NSString *coord = [NSString stringWithFormat:@"{%@,%@}", 
    [NSString stringWithFormat:@"%@", vehicle.vehicleLat], 
    [NSString stringWithFormat:@"%@", vehicle.vehicleLon]]; 
CGPoint point = CGPointFromString(coord); 
myVehicleAnnotation.coordinate = CLLocationCoordinate2DMake(point.x, point.y); 

Я предлагаю это более прямой и менее таинственное подход:

myVehicleAnnotation.coordinate = CLLocationCoordinate2DMake(
    [vehicle.vehicleLat doubleValue], [vehicle.vehicleLon doubleValue]); 
+0

+ 1 Анна! Большое спасибо, я обновил свой вопрос, чтобы отразить предложенные изменения; кроме того, для перемещения [self.mapView removeAnnotation: annotation]; на основной поток, чтобы избежать неприятного мутированного исключения Array. Это отличный стартер, и, как идея поддержания двух наборов коллекций автомобилей, отслеживать фактические и прошлые местоположения, а затем обновлять/удалять/добавлять вместо того, чтобы срывать и восстанавливать аннотации. Однако это решение является оптимальным, карта реагирует и пользовательский интерфейс скользит с взаимодействием, поэтому мне, возможно, не придется реорганизовывать решение. – CampbellGolf

+1

ОБНОВЛЕНИЕ: Я закончил переписывать addVehiclesToMap, чтобы проверить ADD/DELETE/UPDATE для аннотаций. Основная причина - аннотация annotationView.canShowCallout = YES стала проблемой, когда аннотация была удалена и добавлена. Если пользователь вывел вызов аннотации, он исчезнет, ​​когда координация аннотации будет удалена/добавлена ​​повторно ... не отличная пользовательская IMO. – CampbellGolf

Смежные вопросы