问题描述
我正在尝试构建一个通过覆盖图块循环的应用程序。问题在于地图显示时,地图图块需要永久重新加载。解决此问题的最佳方法是什么?我不确定这是否是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
}