8

カスタムMKOverlay/MKOverlayViewを使用して、非同期で読み込まれる独自のタイルで Google ベースマップを完全にカバーしています。オーバーレイ ビューへの呼び出しを受け取ったときにアンロードされたタイルを要求しcanDrawMapRect:zoomScale:(その場合は FALSE を返します)、setNeedsDisplayInMapRect:zoomScale:タイルが利用可能になったら呼び出すというパターンに従います。

これはすべて一般的に機能し、シミュレーターでは完全に機能しているように見えます。

ただし、デバイスでは、オーバーレイに「穴」 (タイルが見つからない) が表示されることがあります。

タイルが要求され、要求が完了したことがわかります。を呼び出していること、およびで提供されたオリジナルをsetNeedsDisplayInMapRect:zoomScale:渡していることがわかります。しかし、オーバーレイがそのタイルを再描画するように求められることは決してないこともわかります (そのタイルに対して再度呼び出されることもありません) 。MKMapRectMKZoomScalecanDrawMapRect:zoomScale:canDrawMapRect:zoomScale:drawMapRect:zoomScale:inContext:

これがなぜ起こっているのか、そしてそれを修正する方法を理解する必要があります。

私の MKOverlayView サブクラスからの関連コードは次のとおりです。

- (BOOL) canDrawMapRect: (MKMapRect) mapRect zoomScale: (MKZoomScale) zoomScale 
{
    NSUInteger zoomLevel = [self zoomLevelForZoomScale:zoomScale];
    CGPoint mercatorPoint = [self mercatorTileOriginForMapRect:mapRect];
    NSUInteger tilex = floor(mercatorPoint.x * [self worldTileWidthForZoomLevel:zoomLevel]);
    NSUInteger tiley = floor(mercatorPoint.y * [self worldTileWidthForZoomLevel:zoomLevel]);

    NSURL* tileUrl = [self tileURLForZoomLevel: zoomLevel tileX: tilex tileY: tiley];

    ASIHTTPRequest* tileRequest = [ASIHTTPRequest requestWithURL: tileUrl];
    tileRequest.downloadCache = [ASIDownloadCache sharedCache];
    [tileRequest setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];

    if ( NO == [[ASIDownloadCache sharedCache] isCachedDataCurrentForRequest: tileRequest] )
    {
        [tileRequest setCachePolicy: ASIAskServerIfModifiedWhenStaleCachePolicy];
        tileRequest.delegate = self;
        tileRequest.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                [NSValue value: &mapRect withObjCType: @encode( MKMapRect )],       @"mapRect",
                                [NSValue value: &zoomScale withObjCType: @encode( MKZoomScale )],   @"zoomScale",
                                [NSNumber numberWithInt: tilex], @"tilex",
                                [NSNumber numberWithInt: tiley], @"tiley",
                                nil];

        [_tileRequestStack addOperation: tileRequest];

        NSLog( @"canDrawMapRect: %d, %d - REQUESTING", tilex, tiley );

        return NO;
    }

    NSLog( @"canDrawMapRect: %d, %d - READY", tilex, tiley );

    return YES;
}

- (void) drawMapRect: (MKMapRect) mapRect zoomScale: (MKZoomScale) zoomScale inContext: (CGContextRef) context 
{
    NSUInteger zoomLevel = [self zoomLevelForZoomScale:zoomScale];

    CGPoint mercatorPoint = [self mercatorTileOriginForMapRect:mapRect];

    NSUInteger tilex = floor(mercatorPoint.x * [self worldTileWidthForZoomLevel:zoomLevel]);
    NSUInteger tiley = floor(mercatorPoint.y * [self worldTileWidthForZoomLevel:zoomLevel]);

    NSLog( @"drawMapRect:  %d, %d", tilex, tiley );

    NSURL* tileUrl = [self tileURLForZoomLevel: zoomLevel tileX: tilex tileY: tiley];
    NSData* tileData = [[ASIDownloadCache sharedCache] cachedResponseDataForURL: tileUrl];

    UIGraphicsPushContext(context);

    if ( tileData != nil )
    {
        UIImage* img = [UIImage imageWithData: tileData];

        if ( img != nil )
        {
            [img drawInRect: [self rectForMapRect: mapRect] blendMode: kCGBlendModeNormal alpha: 1.0 ];
        }
        else 
        {
            NSLog( @"oops - no image" );
        }

        CGSize s = CGContextConvertSizeToUserSpace( context, CGSizeMake( 40, 1 ));

        UIFont* f = [UIFont systemFontOfSize: s.width];

        [[UIColor blackColor] setFill];

        [[NSString stringWithFormat: @"%d,%d", tilex, tiley] drawInRect: [self rectForMapRect: mapRect] withFont: f];
    }

    UIGraphicsPopContext();
}


- (void) requestFinished: (ASIHTTPRequest *) tileRequest
{
    NSValue* mapRectValue =  [tileRequest.userInfo objectForKey: @"mapRect"];
    MKMapRect mapRect;  [mapRectValue getValue: &mapRect];

    NSValue *zoomScaleValue = [tileRequest.userInfo objectForKey:@"zoomScale"];
    MKZoomScale zoomScale; [zoomScaleValue getValue: &zoomScale];

    NSLog( @"requestFinished: %d, %d, %lf", 
          [[tileRequest.userInfo objectForKey:@"tilex"] intValue], 
          [[tileRequest.userInfo objectForKey:@"tiley"] intValue], 
          zoomScale  );

    [self setNeedsDisplayInMapRect: mapRect zoomScale: zoomScale];
}

編集: これが問題である可能性が高いと推測しています。

4

2 に答える 2

1

ここで説明した問題と非常によく似た問題が発生しました。私の場合、目的の動作を再現できませんでした(http://developer.apple.com/library/ios/documentation/MapKit/Reference/MKOverlayView_class/Reference/Reference.html#//apple_ref/occ/instm/で説明されています) MKOverlayView / setNeedsDisplayInMapRect:zoomScale :)可能な限り最も単純なコードでも、次のようになります。

- (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale {
    NSLog(@"This should trace forever");

    [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale];
    return NO;
}

または私の元のコードに近い:

- (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale {
    NSLog(@"This should trace forever");

    [SomeAsynchronousRequestWithCompletionHandler:^{
        [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale];
    }];
    return NO;
}

どちらの場合も、setNeedsDisplayInMapRect:zoomScale:が一度も呼び出されたことはありません。

setNeedsDisplayInMapRect:zoomScaleの実行を開始すると、状況が変わりました。canDrawMapRectが実行されているのと同じキューにディスパッチされたdispatch_async内で、次のようになります。

- (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale {

    dispatch_queue_t queue = dispatch_get_current_queue();

    NSLog(@"This should trace forever");

    dispatch_async(queue, ^{
        [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale];
    });

    return NO;
}

または非同期ジョブが含まれている場合:

- (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale {
    NSLog(@"This should trace forever");

    dispatch_queue_t queue = dispatch_get_current_queue();

    [SomeAsynchronousRequestWithCompletionHandler:^{
        dispatch_async(queue, ^{
            [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale];
        });
    }];
    return NO;
}

dispatch_asyncの使用- 「これは永遠にトレースする必要があります」という文字列が際限なくトレースされているのがわかります。私の元々の問題も完全に解消されました。

後の更新:現在、dispatch_get_main_queue()を使用してsetNeedsDisplayInMapRect:zoomScaleを呼び出しています: like

- (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale {
    NSLog(@"This should trace forever");

    [SomeAsynchronousRequestWithCompletionHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [self setNeedsDisplayInMapRect:mapRect zoomScale:zoomScale];
        });
    }];
    return NO;
}
于 2012-10-26T17:35:19.097 に答える
0

上記の答えは私にはうまくいきませんでした。私が使用した NSLog プリントアウトから、canDrawMapRect で dispatch_get_current_queue() を取得し、後で使用するために保存しているにもかかわらず、別のスレッドが使用されていることがわかりました。これは、少なくとも iPad 4.3 シミュレーターの場合でした。このデバイスでは試していません。

私の解決策は、呼び出す前に x 時間待機するという満足度が低く、エラーが発生しやすい解決策でした。

-(void)setNeedsDisplay:(WebRequest*)webRequest
{  
   [webRequest retain];
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), 
   ^{
      [webRequest autorelease];
      [delegate setNeedsDisplayInMapRect:webRequest.request.requestedMapRect 
                              zoomScale:webRequest.request.requestedScale];
   });     
}
于 2012-11-09T19:28:42.143 に答える