如何解决使用网络图像作为 MapKit 注释之前使用图像编辑
我想在 Apple Map 上使用网络图像作为注释。在我的应用程序的其他部分,我使用的是 SDWebImageSwiftUI,所以我也想在这里使用 SDWebImage。我知道 MapKit 有一个 SDWebImage 插件,但看起来我不能在显示之前对图像进行一些操作。我要实现的效果非常接近Facebook Messenger应用:
我将用户的方形图像存储在 DigitalOcean Space(兼容 Amazon S3 的存储桶)中,因此必须从服务器加载。获取图像后,我需要将其变圆并添加白色背景(就像信使应用程序一样)。
我像这样创建注释视图:
class MapUserAnnotationView: MKAnnotationView {
static let reuseId = "quickEventUser"
var photo: String?
override init(annotation: MKAnnotation?,reuseIdentifier: String?) {
super.init(annotation: annotation,reuseIdentifier: reuseIdentifier)
if let ann = annotation as? MapQuickEventUserAnnotation {
self.photo = ann.photo
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareFordisplay() {
super.prepareFordisplay()
image = "???"
// Here I need to load image and make edit (probably)
}
}
我尝试过这样的事情:
override func prepareFordisplay() {
super.prepareFordisplay()
let img = UIImageView()
img.sd_setimage(with: URL(string: photo ?? ""),completed: {_,_,_ in })
image = img.image
}
但是它不工作(不显示注释,不调用完成并且没有图像加载错误日志)
我知道我遗漏了一些微不足道的东西,但我对 MapKit 的经验很少,而且我找不到任何关于实现类似内容的来源。
我应该如何实现它?我的思维方式是否接近正确的解决方案?
解决方法
在您的 prepareForDisplay
方法中,您尝试使用 SDWebImage 在下载之前下载的图像,因为您在 SDWebImage 完成块之外设置了 image = img.image
。
但是,我认为您无法使用 image
的默认 MKAnnotationView
属性获得所需的样式,因此建议您根据需要创建一个 UIImageView
样式并添加为 MKAnnotationView
的子视图。
请注意,由于您不会使用默认的 image
属性,因此您需要手动设置 MKAnnotationView
的框架。
由于您通常通过出队使用 MKAnnotationView
(即在您在地图上移动时重复使用视图),因此您不应根据 init
中提供的注释更新内容,而是使用 {{ didSet
属性的 1}},因为当 annotation
出列时,此属性将设置为新注释。
示例实现:
MKAnnotationView
,
第一个简短的解决方案
好的,经过一些研究,我找到了简短而干净的解决方案。未调用 .sd_setImage
完成块的问题我根据此 SO 帖子进行了修复:SDWebImage download image completion block not being called。在这之后,给注释设置样式真的很容易——我需要的一切都已经包含在 SDWebImage 中。我的 prepareForDisplay()
函数最终看起来像这样:
override func prepareForDisplay() {
super.prepareForDisplay()
let url = URL(string: photo ?? "")
SDWebImageManager.shared.loadImage(with: url,options: .scaleDownLargeImages,progress: nil,completed: { resultImage,_,_ in
DispatchQueue.main.async {
let size = 40
guard let result = resultImage else { return }
guard let resized = result.sd_resizedImage(with: CGSize(width: size,height: size),scaleMode: .aspectFit) else { return }
guard let modified = resized.sd_roundedCornerImage(withRadius: CGFloat(size / 2),corners: .allCorners,borderWidth: 5,borderColor: .white) else { return }
self.image = modified
}
})
}
当然它缺乏对图像加载/修改错误的正确处理(它只是返回,所以我们可以在没有任何注释的情况下结束),但在这个问题中情况并非如此,所以我不会在这里发布它以保留代码干净的。但还有一个问题。用 sd_roundedCornerImage
渲染的边框是内嵌边框,这不是我想要的。为了解决这个问题,我创建了 UIImage 扩展。
第二种解决方案
UIImage 扩展:
import UIKit
extension UIImage {
func transformToMapAnnotation(stroke: CGFloat = 6,color: CGColor = UIColor.white.cgColor) -> UIImage {
UIGraphicsBeginImageContext(CGSize(width: size.width + 2 * stroke,height: size.height + 2 * stroke))
let context = UIGraphicsGetCurrentContext()!
context.setFillColor(color)
let outerRect = CGRect(x: 0,y: 0,width: self.size.width + 2 * stroke,height: self.size.height + 2 * stroke)
let outerPath = UIBezierPath(roundedRect: outerRect,cornerRadius: (size.height + 2 * stroke) / 2)
let innerRect = CGRect(x: stroke,y: stroke,width: self.size.width,height: self.size.height)
let innerPath = UIBezierPath(roundedRect: innerRect,cornerRadius: size.height / 2)
outerPath.fill()
innerPath.addClip()
self.draw(at: CGPoint(x: stroke,y: stroke))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}
}
现在我的 prepareForDisplay()
函数看起来像这样:
override func prepareForDisplay() {
super.prepareForDisplay()
let url = URL(string: photo ?? "")
SDWebImageManager.shared.loadImage(with: url,_ in
DispatchQueue.main.async {
let size = 30
guard let result = resultImage else { return }
guard let resized = result.sd_resizedImage(with: CGSize(width: size,scaleMode: .aspectFit) else { return }
let modified = resized.transformToMapAnnotation()
self.image = modified
}
})
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。