gpt4 book ai didi

ios - MapKit - 更新 MapView 以通过计时器显示移动注释

转载 作者:塔克拉玛干 更新时间:2023-11-02 09:44:08 26 4
gpt4 key购买 nike

我试图实现的最终状态是拥有一个正在运行的计时器来刷新“车辆”的集合 annotations . annotation coordinates使用计时器每 60 秒成功刷新一次,但用户必须调用 mapView:regionWillChangeAnimated和 map View :regionWillChangeAnimated代表们。这些代表正在正确地工作和移动车辆 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;
}
}

计时器用于获取最新的 Vehicles 并将其加载到 NSMutable Array 中调用(void)RefreshVehicles , 这将更新最新的 coordinates对于将用于更新车辆的每个对象 annotation .我正在使用 NSNotification知道什么时候异步网络调用和SQLite更新车辆记录的工作已经完成。当通知触发时,我将删除任何现有车辆 annotations然后更新本地车辆 NSMutable Array , 通过调用 addVehiclesToMap添加新的 annotations到 map :

//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#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 的方法:**发布解决方案备注:实现 Anna 的解决方案更新 map 后 annotationsMain 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 秒刷新一次 map 。到目前为止,可以看到对 annotation 的任何更改位置,我必须触摸屏幕并移动 map 以调用这些删除。我仍然无法在不进行交互的情况下看到车辆自主移动。

发布解决方案备注:我删除了这些代表,因为他们正在对车辆注释执行不必要的更新,当触摸和移动 map 时,图标会闪烁...让计时器完成工作

**// 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 }

这里我什至接近解决方案了吗?提前致谢...

最佳答案

尝试在主线程上调用 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com