我想要达到的最终状态是让一个正在运行的计时器刷新“车辆”annotations 的集合。 annotation coordinates 使用 Timer 每 60 秒成功刷新一次,但用户必须调用 mapView:regionWillChangeAnimated 和 mapView:regionWillChangeAnimated 代表。这些代表正在正确工作并移动 Vehicle annotations ,但我希望 annotations 能够自主移动,而无需用户与屏幕进行交互。
这是我的方法:
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;
}
}
计时器用于通过调用 (void)RefreshVehicles 获取最新的 Vehicles 并将其加载到 NSMutable Array 中,这将更新最新的坐标 用于更新 Vehicle annotation 的每个对象。我正在使用 NSNotification 来了解异步网络调用和 SQLite 何时更新车辆记录完成。当通知触发时,我将删除任何现有的 Vehicle annotations ,然后通过调用 addVehiclesToMap 来更新本地 Vehicle NSMutable Array 以添加新的注释 到 map :
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#pragma mark Notification Listener
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- (void)RefreshVehiclesNSNotification *)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 SOLUTION REMARK:在 Main Thread 上实现 Anna 的解决方案以更新 map annotations 后,我开始收到以下错误:*** Terminating app由于未捕获的异常 'NSGenericException',原因:'*** Collection <__NSArrayM: 0x16d94af0> 在枚举时发生了变异。 '
这是因为我从后台线程的计时器刷新中删除了注释。为了解决这个问题,我也在主线程中实现了 [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 委托(delegate)的代码:
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#pragma mark MKAnnotationView Delegate
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- (MKAnnotationView *)mapViewMKMapView *)theMapView viewForAnnotationid <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;
}
接下来我确实有逻辑要删除,然后使用以下委托(delegate)重新添加 annotations 。这些委托(delegate)工作得很好,但需要用户与屏幕交互。我正在尝试每隔 X 秒刷新一次 map 。到目前为止,要查看对 annotation 位置的任何更改,我必须触摸屏幕并移动 map 以调用这些删除操作。我仍然无法看到车辆自动移动而无需执行交互。
发布解决方案备注:我删除了这些代表,因为他们正在对车辆注释执行不必要的更新,从而在 map 被触摸和移动时使图标闪烁......让计时器完成工作
**// DELETED** -(void)mapViewMKMapView *)theMapView regionWillChangeAnimatedBOOL)animated { ...Annotation tear down and rebuild code here }
**//DELETED** -(void)mapViewMKMapView *)theMapView didUpdateUserLocationMKUserLocation *)userLocation { ...Annotation tear down and rebuild code here }
我是否已经接近解决方案?提前谢谢...
Best Answer-推荐答案 strong>
尝试在主线程上调用 addAnnotation ,以便 UI 更新不会出现延迟:
dispatch_async(dispatch_get_main_queue(), ^{
[self.mapView addAnnotation:myVehicleAnnotation];
});
一个不相关的建议:
无需删除和重新添加车辆注释,您只需更新现有车辆的 coordinate 属性, map View 将自动移动注释 View 。这可能会导致稍微平滑的效果,尽管实现逻辑稍微困难一些(例如,您需要在 map View 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]);
关于ios - MapKit - 更新 MapView 以通过计时器显示移动注释,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/21446917/
|