Setting an icon to GMSMarker
in Google Maps SDK
requires the UIImage
, but currently my requirements are downloading it from a specific URL
The problem is that somehow only the last item sometimes is being shown. This the code on how I create markers (Updated in Swift)
func createMarkers() {
mapView.clear()
let mapCoordinates: [CLLocationCoordinate2D] = coordinates()
let iconURLs: [URL] = imageURLs()
var marker = GMSMarker()
for i in 0..<mapCoordinates.count {
let imageURL = iconURLs[i]
marker = GMSMarker()
marker.position = mapCoordinates[i]
marker.map = mapView
downloadImage(from: imageURL) { image in
marker.icon = image
}
}
}
// It is a helper function calling `SDWebImage` which caches the `UIImage` based on its `URL` string
func downloadImage(from url: URL, completion: @escaping (UIImage) -> Void)
From code provided above, I am having trouble while I am loading the data first time, because pins are showing on map but without image. If I call createMarkers()
again after some time, the icons are loaded correctly.
I don't know why this is happening, any suggestion or hint to make fix this issue?
Just use SDWebimage, and .iconView not .icon
let imageView = UIImageView(image: pinImage)
imageView.sd_setImage(with: URL(string: "https://pbs.twimg.com/profile_images/469017630796296193/R-bEN4UP.png"), placeholderImage: pinImage)
marker.iconView = imageView
Okay back then, I did not know much about concurrency, and seeing all the answers did not actually solved what I really had in mind.
Since the downloading an image from the URL
(downloadImage(from url: URL, completion: @escaping (UIImage) -> Void)
) is a background operation, by the time that the image has been downloaded, marker
has gone out of scope, by being initialized again on the loop.
This way there is no way to know which image was being bound to which marker.
Also regarding Why it works when reloading it?, the SDWebImage
when being reloaded has already download and cached the image, so the next time there is no delay, it's just bounded to the marker
directly.
Move the mutable variable markers as an immutable value inside the loop.
func createMarkers() {
let mapCoordinates: [CLLocationCoordinate2D] = coordinates()
let iconURLs: [URL] = imageURLs()
for i in 0..<mapCoordinates.count {
let imageURL = iconURLs[i]
let marker = GMSMarker()
marker.position = mapCoordinates[i]
marker.map = mapView
applyImage(from: imageURL, to: marker)
}
}
Now you see I introduced a helper called applyImage(from:, to:)
which basically does nothing special but downloads the image from the URL
and binds it to the argument of type GMSMarker
.
func applyImage(from url: URL, to marker: GMSMarker) {
DispatchQueue.global(qos: .background).async {
guard let data = try? Data(contentsOf: url),
let image = UIImage(data: data)?.cropped()
else { return }
DispatchQueue.main.async {
marker.icon = image
}
}
}
Extra there is an extension for cropping the image to 44pt X 44pt, this is not right, but it's just to preview what has been loaded.
extension UIImage {
func cropped() -> UIImage? {
let cropRect = CGRect(x: 0, y: 0, width: 44 * scale, height: 44 * scale)
guard let croppedCGImage = cgImage?.cropping(to: cropRect) else { return nil }
return UIImage(cgImage: croppedCGImage, scale: scale, orientation: imageOrientation)
}
}
NOTE: The whole ViewController code can be found this Gist I wrote!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With