草庐IT

ios - 更新时自定义注释不会在 map View 上更新

coder 2024-01-25 原文

我正在执行 willChangeValueForKeydidChangeValueForKey 调用,但即使使用它们,mapView 委托(delegate)也永远不会运行通过 mapView: viewForAnnotation: 来更新事情。如何强制更新 MKAnnotation

当图像更改时,annotationView.image 不会更新,annotationView.image 也不会更新。据我所知,通过我的 NSLog 行,当 MKAnnotation 更新时,mapView: viewForAnnotation: 不会被再次调用。

.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface MapAnnotation : NSObject <MKAnnotation>

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, retain) NSString *currentTitle;
@property (nonatomic, retain) NSString *currentSubTitle;
@property (nonatomic, retain) NSString *category;
@property (nonatomic, retain) NSString *pin;

- (NSString*) title;
- (NSString*) subtitle;
- (id) initWithCoordinate:(CLLocationCoordinate2D) c;
- (CLLocationCoordinate2D) getCoordinate;
- (void) addPlace:(MapAnnotation*) place;
- (int) placesCount;
- (void) cleanPlaces;
- (UIImage *) getPin;
@end

.m

#import "MapAnnotation.h"

@interface MapAnnotation()
@property (strong) NSMutableArray *places;
@end

@implementation MapAnnotation

@synthesize coordinate      = _coordinate;
@synthesize currentTitle    = _currentTitle;
@synthesize currentSubTitle = _currentSubTitle;
@synthesize places          = _places;
@synthesize category        = _category;
@synthesize pin             = _pin;


- (NSString*) subtitle {
    if ([self placesCount] == 1) {
        return self.currentSubTitle;
    }
    else{
        return @"";
    }
}


- (NSString*) title {
    if ([self placesCount] == 1) {
        return self.currentTitle;
    }
    else{
        return [NSString stringWithFormat:@"%d places", [self placesCount]];
    }
}


- (void)addPlace:(MapAnnotation *)place {
    [self willChangeValueForKey:@"title"];
    [self willChangeValueForKey:@"subtitle"];
    [self.places addObject:place];
    [self didChangeValueForKey:@"title"];
    [self didChangeValueForKey:@"subtitle"];
}


- (CLLocationCoordinate2D)getCoordinate {
    return self.coordinate;
}


- (void)cleanPlaces {
    [self willChangeValueForKey:@"title"];
    [self willChangeValueForKey:@"subtitle"];
    [self.places removeAllObjects];
    [self.places addObject:self];
    [self didChangeValueForKey:@"title"];
    [self didChangeValueForKey:@"subtitle"];
}


- (id)initWithCoordinate:(CLLocationCoordinate2D) c {
    self.coordinate=c;
    self.places=[[NSMutableArray alloc] initWithCapacity:0];
    return self;
}


- (int)placesCount {
    return [self.places count];
}


- (UIImage *)getPin {
    // begin a graphics context of sufficient size
    CGSize size = CGSizeMake(26, 26);
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);

    // get the context for CoreGraphics
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGRect imageRect = CGRectMake(0, 0, size.width, size.height);

    // Draw Dot
    CGRect circleRect = CGRectInset(imageRect, 4, 4);
    [[UIColor blackColor] setStroke];
    [[UIColor yellowColor] setFill];
    CGContextFillEllipseInRect(ctx, circleRect);
    CGContextStrokeEllipseInRect(ctx, circleRect);

    // Dot Content
    [[UIColor blackColor] setStroke];
    [[UIColor blackColor] setFill];
    CGAffineTransform transform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
    CGContextSetTextMatrix(ctx, transform);
    CGContextSetLineWidth(ctx, 2.0);
    CGContextSetCharacterSpacing(ctx, 1.7);
    CGContextSetTextDrawingMode(ctx, kCGTextFill);
    UIFont *font = [UIFont fontWithName:@"Arial" size:11.0];
    if ([self placesCount] != 1) {
        NSString *label = [NSString stringWithFormat:@"%d", [self placesCount]];
        CGSize stringSize = [label sizeWithAttributes:@{NSFontAttributeName:font}];
        [label drawAtPoint:CGPointMake(size.width / 2 - stringSize.width / 2, size.height / 2 - stringSize.height / 2)
            withAttributes:@{NSFontAttributeName:font}];
    } else {
        NSString *label = [NSString stringWithFormat:@"%@", self.pin];
        CGSize stringSize = [label sizeWithAttributes:@{NSFontAttributeName:font}];
        [label drawAtPoint:CGPointMake(size.width / 2 - stringSize.width / 2, size.height / 2 - stringSize.height / 2)
            withAttributes:@{NSFontAttributeName:font}];
    }


    // make image out of bitmap context
    UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();

    // free the context
    UIGraphicsEndImageContext();

    return retImage;
}

@end

map View :viewForAnnotation:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
    MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:@"MyAnnoation"];
    if(!annotationView) {
        annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"MyAnnoation"];
        annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    }

    annotationView.enabled = YES;
    if ([(MapAnnotation*)annotation placesCount] == 1) {
        annotationView.canShowCallout = YES;
    }
    annotationView.image = [(MapAnnotation*)annotation getPin];
    NSLog(@"%@", [annotation title]);

    return annotationView;
}

最佳答案

如评论中所述,除了 titlesubtitle 之外,注释属性的更改不会自动导致 View 更新。

更改 titlesubtitle 将在显示时自动更新标注,只要更改导致 KVO 通知(例如,如果使用带有 ann.title = xxx 或通过手动发出 KVO 通知作为问题中的代码,因为您有自定义 getter)。

要自动更新图像,您可能需要使用自定义注释 View 来观察底层注释属性的变化并自行刷新(在 init 中调用 addObserver , 在 observeValueForKeyPath 更新图像, 在 dealloc 调用 removeObserver).您也可以使用非 KVO 方法来完成它,例如自定义通知或委托(delegate)(但仍然使用自定义注释 View )。

在现有代码中,无需创建自定义注解 View 和使用KVO等即可快速手动更新注解 View 的方法是在注解更新后立即获取注解的当前 View 并更新图像 属性。只要 viewForAnnotation 中的代码与设置 image 的逻辑相同,这就应该有效。

我假设在应用程序的某处(有时注释已添加之后),它正在通过调用 addPlace 更新注释中的位置。之后,您可以尝试以下操作:

[someExistingAnnotation addPlace:anotherPlace];

//obtain the current view of someExistingAnnotation...
MKAnnotationView *av = [mapView viewForAnnotation:someExistingAnnotation];

//update its image property to force refresh...
av.image = [someExistingAnnotation getPin];
//also set canShowCallout (it might have been NO)


不相关,但当前的 viewForAnnotation 没有正确处理可能的 View 重用和/或自定义注释以外的注释类型(例如用户位置蓝点,这会导致当前代码崩溃)。我建议将其更改为:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
    if (! [annotation isKindOfClass:[MapAnnotation class]])
    {
        //annotation is NOT a "MapAnnotation", return nil for default view...
        return nil;
    }

    MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:@"MyAnnoation"];
    if(!annotationView) {
        annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"MyAnnoation"];
        annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    }

    //update view's annotation to current in case it's being re-used
    annotationView.annotation = annotation;

    annotationView.enabled = YES;
    if ([(MapAnnotation*)annotation placesCount] == 1) {
        annotationView.canShowCallout = YES;
    }
    else {
        annotationView.canShowCallout = NO;
    }

    annotationView.image = [(MapAnnotation*)annotation getPin];
    NSLog(@"%@", [annotation title]);

    return annotationView;
}

关于ios - 更新时自定义注释不会在 map View 上更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20026321/

有关ios - 更新时自定义注释不会在 map View 上更新的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  5. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  6. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  7. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  8. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  9. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  10. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

随机推荐