2012-08-27 2 views
0

사용자가지도를 스크롤 할 때 볼 수있는 무한한 양의 주석으로 MKMapView를 채우려고합니다. 뷰를 표시하려면 사용자가 너무 멀리 확대해야합니다. 앱이 너무 많아서 처리 할 수 ​​없으면 한 번에 약 20 개만 표시되므로 분명히 표시해야합니다.무한한 임의의 주석이있는 MKMapview

무작위로지도 전체에서 무작위로 반복해야하는 약 100 개의 개체 배열이 있습니다. 이것들은 MKMapView의 visibleMapRect 속성을 사용하여 런타임 중에 생성되어 필요한 것들만 생성합니다. 이전에 생성 된 객체를 다시 생성하지 못하도록 캐시 사전도 구현했습니다. 여기 내 간단 현재 코드입니다 :

@property (nonatomic, strong) NSArray *mapObjects; //Contains 100 objects to be repeated 
@property (nonatomic, strong) NSMutableDictionary *cache; //Cache already created mapObjects 

//Constants used 
int const MapDensity = 5000.0; //Density of annotations 
int const MapZoomMax = 30000.0; //Max zoom level to show annotations 

- (void)loadLocations { 
    MKMapRect rect = [mapView visibleMapRect]; 

    if (rect.size.width > MapZoomMax) { 
    [self performSelectorOnMainThread:@selector(removeAllAnnotations) withObject:nil waitUntilDone:NO]; 
     return; 
    } 

    rect.origin.x = MapDensity*floor(rect.origin.x/MapDensity); 
    rect.origin.y = MapDensity*floor(rect.origin.y/MapDensity); 

    MKMapPoint pointLocation = rect.origin; 
    NSMutableArray *locationsArray = [NSMutableArray array]; 

    while (pointLocation.y < rect.origin.y+rect.size.height) { 
     while (pointLocation.x < rect.origin.x+rect.size.width) { 
      int cacheKey = pointLocation.x*pointLocation.y; 
      if (![self.cache objectForKey:[NSNumber numberWithInt:cacheKey]]) { 

       //Adjust for randomness 
       MKMapPoint pointLocationAdjusted = pointLocation; 
       pointLocationAdjusted.x += arc4random()%MapDensity; 
       pointLocationAdjusted.y += arc4random()%MapDensity; 

       //Create annotation 
       GLAnnotation *annotation = [[GLAnnotation alloc] init]; 
       [annotation setCoordinate:MKCoordinateForMapPoint(pointLocationAdjusted)]; 
       [annotation setMapObject:[self.mapObjects objectAtIndex:(arc4random()%[mapObjects count])]]; 
       [locationsArray addObject:annotation]; 

       [self.cache setObject:annotation forKey:[NSNumber numberWithInt:cacheKey]]; 
      } else { 
       [locationsArray addObject:[self.cache objectForKey:[NSNumber numberWithInt:cacheKey]]]; 
      } 
      pointLocation.x += MapDensity; //Go to next X 
     } 
     pointLocation.x = rect.origin.x; //Restart X 
     pointLocation.y += MapDensity; //Go to next Y 
    } 

    [self performSelectorOnMainThread:@selector(addAnnotations:) withObject:locationsArray waitUntilDone:NO]; 
} 

- (void)addAnnotations:(NSArray *)annotations { 
    NSMutableArray *newAnnotations = [NSMutableArray array]; 
    for (id annotation in annotations) { 
     if (![mapView.annotations containsObject:annotation]) { 
      [mapView addObject:annotation]; 
     } 
    } 
    [mapView addAnnotations:newAnnotations]; 
} 

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { 
    [self performSelectorInBackground:@selector(loadLocations) withObject:nil]; 
} 

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation { 
    if([annotation isKindOfClass:[GLAnnotation class]]){ 
     static NSString *annotationIdentifier = @"AnnotationIdentifier"; 

     MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier]; 
     if(!annotationView){ 
      annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier]; 
      annotationView.canShowCallout = YES; 
     } 

     annotationView.image = [(GLAnnotation *)annotation picture]; 
     return annotationView; 
    } 
    return nil; 
} 

코드는 대부분의 시간을 작동하는 것 같다하지만 때때로 지연 스크롤지도를 만드는 일반적으로 앱 충돌한다 (I는 백그라운드 스레드에서 실행하는 것을 해결하기 위해 노력했다) , 메모리 문제 (내가 캐시를 사용하여 수정하려고)에 의해 발생하는 것 같습니다. EXC_BAD_ACCESS를 제외한 모든 오류 메시지를 출력하지 않습니다. 누군가가 그 양의 주석을 올바르게 관리하는 방법을 말해 줄 수 있다면 매우 감사 할 것입니다.

답변

0

내가 생각하는 것보다 25,000,000 개 더 많은 항목이 생성 될 수 있습니다. 하지만 제 수학을 확인해주세요. rect.origin에서 시작하여 MapDensity로 증가하는 각 좌표 쌍에 대해 하나의 주석을 작성합니다. 그래서 각 5000x5000 블록에 대해 하나의 개체. 첫 번째 캐시 된 (0,0). 그러나 맵 뷰가 하나의 맵핑만으로 변경된 경우 (0,1) 및 다음 5000x5000 블록에 대해 완전히 새로운 개체를 갖게됩니다. 아마도 당신은 출발점을 다음으로 가장 낮은 5000 마포트 (mappoint)로 바꾸고 거기에서 증가시켜야합니다. 이렇게하면 시작 지점이 5000x5000 블록을 벗어날 때까지 같은 개체를 계속 재사용 할 수 있습니다.

또한 캐시의 해시는 충돌을 초래합니다. (1,12), (2,6), (3,4), (4,3), (6,2), (12,1), (-1, -12), (-2, -6) etc etc, 모든 해쉬는 같은 값을가집니다. 키는 문자열이 될 수 있기 때문에 [NSString stringWithFormat : @ "% d_ % d", x, y] 대신 사용할 수 있습니다.

+0

예, 캐시 버그가 맞습니다. 그 부분은 제대로 작동하는 것처럼 보였으므로 눈치 채지 못했지만 사용자가 민감한 위치에 있었다면 쉽게 알아 차릴 수있었습니다. 고맙습니다! – gabriellanata

+0

첫 번째 부분에서는 시작점을 이동하기 위해 다음 코드를 추가했지만 여전히 많은 메모리 오류가 발생합니다. 'rect.origin.x - = MapDensity * floor (rect.origin.x/MapDensity); ' 'rect.origin.y - = MapDensity * floor (rect.origin.y/MapDensity); ' – gabriellanata

+0

당신이 당신의 시내를 건너고있는 것 같습니다. regionDidChangeAnimated 호출은 ui 스레드에서 발생합니다. 그런 다음 백그라운드 스레드에서 loadLocations를 실행하지만 주석을 삭제하고 mapview에 항목을 추가하므로 주/ui 스레드에서 다시 돌아와야합니다. 위치 맵이란 무엇입니까? – Craig