微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用网络图像作为 MapKit 注释之前使用图像编辑

如何解决使用网络图像作为 MapKit 注释之前使用图像编辑

我想在 Apple Map 上使用网络图像作为注释。在我的应用程序的其他部分,我使用的是 SDWebImageSwiftUI,所以我也想在这里使用 SDWebImage。我知道 MapKit 有一个 SDWebImage 插件,但看起来我不能在显示之前对图像进行一些操作。我要实现的效果非常接近Facebook Messenger应用:

Facebook Messenger screenshot

我将用户的方形图像存储在 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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?