iOS 13.2 MKTileOverlay 偶尔不会渲染

Posted

技术标签:

【中文标题】iOS 13.2 MKTileOverlay 偶尔不会渲染【英文标题】:iOS 13.2 MKTileOverlay occasionally won't render 【发布时间】:2019-12-30 03:31:06 【问题描述】:

我遇到了一个问题,在 ios 13.2(可能也来自 iOS 13)中,使用 MKTileOverlay 加载离线地图图块偶尔无法渲染,使图块留空,似乎没有问题MKTileOverlay 的子类,因为它在 iOS 12 及更低版本中运行良好。我有 2 个MKTileOverlay 类(1 个添加网格和 1 个加载地图瓦片文件,默认为 MKTileOverlay),两者都无法在默认为 MKTileOverlayRenderer 的空白瓦片上加载,其他覆盖似乎很好。

如果我转到主屏幕并返回应用程序,问题似乎会自行解决,从而导致图块重新加载。这是 iOS MapKit 本身的错误吗?有人对此有临时解决方案吗?谢谢。

添加叠加层代码:

let overlay = MKTileOverlay(urlTemplate: urlTemplate)
overlay.canReplaceMapContent = true
overlay.maximumZ = 19
mapView.insertOverlay(overlay, at: 0, level: .aboveLabels)

渲染器:

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer 
    if overlay is MKTileOverlay 
        let renderer = MKTileOverlayRenderer(tileOverlay: overlay as! MKTileOverlay)
        return renderer
    

    return MKOverlayRenderer()

【问题讨论】:

您找到解决方案了吗?我在 Xamarin.iOS 中使用 MapKit 的长期应用程序中遇到了完全相同的问题。它到了我把它从商店里撤下来的阶段,因为苹果似乎在 13.2 前后引入了很多问题。在空白图块的情况下,我将其跟踪到 MKOverlayTileRenderer,即使提供了有效数据,它也无法加载图像。 @PhilJohn 很遗憾,不,我必须在 mapViewDidFinishRenderingMap(mapView:fullyRendered:) 上实施一个不太好的解决方法,手动调用 renderer.reloadData(),它会一直闪烁,但它应该在重新加载后消除空白图块 【参考方案1】:

这显然是 MapKit 问题/错误。

自 2020 年 12 月 9 日起,我还开立了反馈票。

这个问题的根源不是很确定。

MapKit 和特别是 MKTileOverlay 总是有/有一些“重”瓷砖的问题,比如 PNG 24 位。当 MKTileOverlay 使用 PNG(重图块)时,图块有时会闪烁,并且地图会不断重新加载,尤其是在宽屏幕(iPad pro 等)的情况下。

因此,由于 JPEG 切片通常比 PNG 更轻,因此 JPEG 可能是一种解决方法。

但是,这个新的 iOS 13.2+ 问题不一样了!不渲染随机图块。如果您删除并读取了 MKTileOverlay 或调用 MKTileOverlayRenderer 的 reloadData 方法,将渲染丢失的瓦片,并且将其他随机瓦片丢失。

问题的真正解决方法是开反馈票:https://feedbackassistant.apple.com

编辑:我刚刚尝试将我的 8bit PNG 替换为 85% JPEG,这是我在我的工单中发送给 Apple 的非常简单的 MKTileOverlay 项目示例。同样的问题...没有改善。

编辑 2:将 NSData 加载到 UIImage 中,然后使用 UIImageRepresentationJPEG 似乎可以解决问题...丑陋...

- (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData * _Nullable, NSError * _Nullable))result

    NSString *tilePath = [self PATHForTilePath:path];
    NSData *data = nil;

    if (![[NSFileManager defaultManager] fileExistsAtPath:tilePath])
    
        NSLog(@"Z%ld/%ld/%ld does not exist!", path.z, path.x, path.y);
    
    else
    
        NSLog(@"Z%ld/%ld/%ld exist", path.z, path.x, path.y);

        UIImage *image = [UIImage imageWithContentsOfFile:tilePath];
        data = UIImageJPEGRepresentation(image, 0.8);
        // Instead of: data = [NSData dataWithContentsOfFile:tilePath];

        if (data == nil)
        
            NSLog(@"Error!!! Unable to read an existing file!");
        
    

    dispatch_async(dispatch_get_main_queue(), ^
        result(data, nil);
    );

【讨论】:

与上述方法非常相似,但我也没有看到任何改进,在我的地图中有些地方大约有 50 针,如果我缩小它总是会导致空白图块......仍然是唯一的使其正常工作的修复方法是在用户停止移动地图后继续重新加载。【参考方案2】:

正如我在对原始问题的评论中指出的那样,我遇到了同样的问题,但现在基本上已经解决了,所以我想我会发布对我有用的东西。

我的问题发生在以下方法中

- (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData * __nullable tileData, NSError * __nullable error))result

自定义磁贴即使在显示看似有效的 NSData 时也无法加载的位置。

我发现如果我对自定义切片使用 jpegs 而不是 pngs,问题就会减少,但只有当我改变处理切片数据的方式时,问题才基本消失。 (我主要是因为我仍然偶尔会得到卸载的瓷砖,但我会说它比我以前得到它们的频率少了 100 倍)。

下面的方法是我的Xamarin.iOS实现,不过你应该可以看到Swift或者Objective C的原理。

关键在于创建 NSData 的方式不同。我没有调用 UrlForTilePath 方法,而是从平铺路径创建 UIImage,然后使用 UIImageJPEGRepresentation(C# 中的 AsJPEG)创建 NSData。

   public override void LoadTileAtPath(MKTileOverlayPath path, MKTileOverlayLoadTileCompletionHandler result)
    
        //I was using this prior to ios 13.2

        //NSUrl url = this.URLForTilePath(path);
        //NSData tileData = NSData.FromFile(url.AbsoluteString);
        //result(tileData, null);

        //Now I use this

        String folderPath = "tiles/" + path.Z + "/" + path.X + "/";
        String tilePath = NSBundle.MainBundle.PathForResource(path.Y.ToString(), "jpg", folderPath);
        String blankPath = NSBundle.MainBundle.PathForResource("tile", "jpg");

        try
        
            //does the file exist?
            UIImage tile;
            if (File.Exists(tilePath))
            
                tile = UIImage.FromFile(tilePath);
                if (tile == null)
                
                    Console.WriteLine("Error Loading " + path.Z + " " + path.Y + " " + path.X);
                    //This may be redundant, as I'm not getting any errors here, even when the tile doesn't display
                
            
            else
            
                tile = UIImage.FromFile(blankPath);
            

            NSData tileData = tile.AsJPEG();
            result(tileData, null);
        
        catch (Exception ex)
        

        
    

【讨论】:

感谢您的意见,我已经尝试过同样的事情,但它仍然经常发生在我身上:(尤其是在有很多注释的地方。 可能因为我还在用png图片,我只在loadTile期间转成jpeg,可能不会有任何效果。 我确实注意到当我将叠加层和注释放在一起时情况会更糟,但在我的情况下这种情况并不常见。我确实向 Apple 提交了反馈报告。

以上是关于iOS 13.2 MKTileOverlay 偶尔不会渲染的主要内容,如果未能解决你的问题,请参考以下文章

iOS 7 有没有办法知道 MKTileOverlay 何时完成加载图块?

iOS:在地图上绘制 MKTileOverlay 导致 CPU 使用率和内存高。我找不到原因。?

MKTileOverlay 未定义

带有自签名 SSL 证书的 iOS MKTileOverlay

缓存切片的 MKTileOverlay 子类

Swift 中的 MKTileoverlay 问题