MapKit Tile Overlay加载时间较长

问题描述

我正在尝试构建一个通过覆盖图块循环的应用程序。问题在于地图显示时,地图图块需要永久重新加载。解决此问题的最佳方法是什么?我不确定这是否是MapKit会自行处理的缓存问题。我的猜测是这是Swift重新绘制的问题。我的代码在下面,感谢您的帮助。

//  copyright 2020 Oklahoma Weather Blog
//

import SwiftUI

import MapKit
import SwiftSoup


/*
struct RadarMapView: UIViewRepresentable {

    
    var coordinate: CLLocationCoordinate2D
    var tileRenderer = MKOverlayRenderer()
    
    func mapView(mapView: MKMapView,rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
        guard let tileOverlay = overlay as? MKTileOverlay else {
            return MKOverlayRenderer()
        }
        
        return MKTileOverlayRenderer(tileOverlay: tileOverlay)
    }
    
    func makeUIView(context: Context) -> MKMapView {
        var template = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
        template = "https://tilecache.rainviewer.com/v2/ radar/1600575600/512/{z}/{x}/{y}/6/0_1.png"
        let overlay = MKTileOverlay(urlTemplate:template)

      overlay.canReplaceMapContent = false
        let mapView = MKMapView(frame: .zero)
        var renderedOverlay = MKTileOverlayRenderer(tileOverlay: overlay)
        mapView.addOverlay(overlay,level: .aboveLabels)
        mapView.setNeedsdisplay()
        return mapView
    }

    func updateUIView(_ view: MKMapView,context: Context) {

        let span = MKCoordinateSpan(latitudeDelta: 0.3,longitudeDelta: 0.3)
        let region = MKCoordinateRegion(center: coordinate,span: span)
        
    
        view.setRegion(region,animated: true)
    }
}
 */

func getTimesURL() -> [String] {
    let myURLString = "https://api.rainviewer.com/public/maps.json"
    guard let myURL = URL(string: myURLString) else {
        printToConsole("Error: \(myURLString) doesn't seem to be a valid URL")
        return []
    }

    do {
        let myHTMLString = try String(contentsOf: myURL)
        do {
           let doc: Document = try SwiftSoup.parse(myHTMLString)
            
            let text = try doc.text()
            let resultArray = text.trimmingCharacters(in: CharacterSet(charactersIn: "[]"))
                .components(separatedBy:",")
            return resultArray

            
        } catch Exception.Error( _,let message) {
            printToConsole(message)
        } catch {
            printToConsole("error")
        }
    } catch let error as NSError {
        printToConsole("Error: \(error)")
    }
    return []
    
}




func getoverlays() -> [MKTileOverlay] {
    var overlays: [MKTileOverlay] = []
    for time in getTimesURL() {
        let template = "https://tilecache.rainviewer.com/v2/radar/\(time)/256/{z}/{x}/{y}/7/1_1.png"
        
        let overlay = MKTileOverlay(urlTemplate:template)
        overlays.append(overlay)
    }

    return overlays
    
}


struct RadarView: View {
    private static let mapStyles: [MKMapType] = [.hybrid,.hybridFlyover,.mutedStandard,.satellite,.satelliteFlyover,.standard]
    @State var mapTime = "1600585200"
    

    let cache = NSCache<Nsstring,MKTileOverlay>()
    
    @AppStorage("selectedMapStyle") var selectedMapStyle = 0
    
    @State private var showingSheet = false
    
    private static var overlayArray: [MKTileOverlay] {
        getoverlays()
    }
    
    private static var timeArray: [String] {
        getTimesURL()
    }
    
    func datetoString(_ epoch: String) -> String{


        let dateFormatterPrint = DateFormatter()
        dateFormatterPrint.dateFormat = "MM/dd hh:mm a"

        let date = Date(timeIntervalSince1970: TimeInterval(Int(epoch)!))
        return dateFormatterPrint.string(from: date)

    }
    
    
    @State private var timeIndex: Double = 0
    @State private var loopMap: Bool = false
    @State var radarTimer: Timer?

    
    
    var body: some View {
        vstack{
            ZStack(alignment: .top) {
                RadarMapView(mapStyle: RadarView.mapStyles[selectedMapStyle],overlay: RadarView.overlayArray[Int(timeIndex)]).edgesIgnoringSafeArea(.bottom)
                
            HStack{
                vstack{
                    Slider(value: $timeIndex.animation(.linear),in: 0...9,step: 1)
                    Text("\(datetoString(RadarView.timeArray[Int(timeIndex)]))")
                }
                
                Button(action: {
                    loopMap.toggle()
                    if loopMap {
                        radarTimer = Timer.scheduledTimer(withTimeInterval: 1.0,repeats: true) { _ in
                                // do something here
                            if RadarView.overlayArray.count > 0 {
                                withAnimation{
                                timeIndex = Double((Int(timeIndex) + 1) % RadarView.overlayArray.count )
                                }
                            }

                            }
       

                    } else {
                        radarTimer?.invalidate()
                    }
                },label: {
                    
                    if !loopMap { Text("Loop") }
                    else { Text("Pause") }
                })
                

            }.padding(.horizontal).padding(.horizontal).background(Color.init(UIColor.systemBackground).opacity(0.75))

            }
            
            HStack(){
                Spacer()
                Button(action: {
                    /*
                    selectedMapStyle = (selectedMapStyle + 1) % mapStyles.count
                    */
                    showingSheet.toggle()
                },label: {
                    Image(systemName: "map.fill").resizable()
                        .renderingMode(.template)
                        .font(.title)
                        .foregroundColor(Color.primary)
                        .frame(width: 20,height: 20)
                }).padding()
            }.padding(.horizontal).padding(.bottom).background(Color.init(UIColor.systemBackground).opacity(0.75))

        }.actionSheet(isPresented: $showingSheet) {
            ActionSheet(title: Text("What map style do you want?"),message: Text("Please select one option below"),buttons: [
                .default(Text("Muted")) { self.selectedMapStyle = 2 },.default(Text("Satellite")) { self.selectedMapStyle = 3 },.default(Text("Satellite w/ Roads")) { self.selectedMapStyle = 0 },.default(Text("Satellite 3-D")) { self.selectedMapStyle = 4 },.default(Text("3-D Satellite w/ Roads")) { self.selectedMapStyle = 1 },.default(Text("Standard")) { self.selectedMapStyle = 5 },.cancel(Text("dismiss"))
            ])
        }.edgesIgnoringSafeArea(.bottom).navigationBarTitle("Radar")
    }
}

struct RadarMapView: UIViewRepresentable {
    var mapStyle: MKMapType
    var overlay: MKTileOverlay
    

    class Coordinator: NSObject,MKMapViewDelegate {
        var parent: RadarMapView

        init(_ parent: RadarMapView) {
            self.parent = parent
        }

        func mapView(_ mapView: MKMapView,rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            
            let renderer = MKTileOverlayRenderer(overlay: overlay)
            
            return renderer
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> MKMapView {
        return MKMapView()
    }
    
    
    

    func updateUIView(_ mapView: MKMapView,context: Context) {
        
        // var template = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
        //1600626600
    
        mapView.mapType = self.mapStyle
        mapView.delegate = context.coordinator

        let overlays = mapView.overlays
        mapView.addOverlay(overlay)
        
        let regionRadius: CLLocationdistance = 50000
        let location = CLLocationCoordinate2D(latitude: 33.7490,longitude: -84.3880)
        let coordinateRegion = MKCoordinateRegion(center: location,latitudinalMeters: regionRadius * 2.0,longitudinalMeters: regionRadius * 2.0)
        mapView.setRegion(coordinateRegion,animated: true)
        for overlay in overlays {
            // remove all MKpolyline-Overlays
            if overlay is MKTileOverlay {
                mapView.removeOverlay(overlay)
            }
        }
        
    }
}

struct RadarView_Previews: PreviewProvider {
    static var previews: some View {
        RadarView()
    }
}

解决方法

几天前(使用不同的图块服务器)我遇到了类似的问题,并通过使用512px图块解决了该问题。

我的理论是,几乎没有人使用256像素图块,因此服务器不会缓存它们。

做类似的事情

func getOverlays() -> [MKTileOverlay] {
    var overlays: [MKTileOverlay] = []
    for time in getTimesURL() {
        let template = "https://tilecache.rainviewer.com/v2/radar/\(time)/512/{z}/{x}/{y}/7/1_1.png"
    
        let overlay = MKTileOverlay(urlTemplate:template)
        overlay.tileSize = CGSize(width: 512,height: 512)
        overlays.append(overlay)
    }

    return overlays

}