【问题标题】:Using web image as MapKit annotation (with image edit before)使用 web 图像作为 MapKit 注释(之前有图像编辑)
【发布时间】:2021-09-07 14:52:57
【问题描述】:

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

我将用户的方形图像存储在 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 的经验很少,我找不到任何关于实现类似东西的资料。

我应该如何实现它?我的思维方式是否接近正确的解决方案?

【问题讨论】:

  • 你真的在 SwiftUI 应用中使用它吗?
  • 是的,我使用UIViewRepresentable 来显示MKMapView

标签: swift swiftui mapkit sdwebimage mapkitannotation


【解决方案1】:

在您的 prepareForDisplay 方法中,您尝试在 SDWebImage 下载之前使用 SDWebImage 下载的图像,因为您在 SDWebImage 完成块之外设置了 image = img.image

但是,我认为您无法使用 MKAnnotationView 的默认 image 属性获得所需的样式,因此建议创建一个您希望的样式的 UIImageView 并添加为MKAnnotationView

请注意,由于您不会使用默认的image 属性,因此您需要手动设置MKAnnotationView 的框架。

由于您通常通过 dequeuing 使用 MKAnnotationView(即它会在您在地图上移动时重用视图),因此您不应根据 init 中提供的注释更新内容,而应使用 @ 的 didSet 987654332@ 属性,因为当MKAnnotationView 出列时,该属性将设置为新的注解。

示例实现:

class MapUserAnnotationView: MKAnnotationView {
    static let reuseId = "quickEventUser"
    var photo: String?
    override var annotation: MKAnnotation? {
        didSet {
            if let ann = annotation as? MapQuickEventUserAnnotation {
                self.photo = ann.photo
            }
        }
    }

    let imageView: UIImageView = {
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        imageView.layer.cornerRadius = 25.0
        imageView.layer.borderWidth = 3.0
        imageView.layer.borderColor = UIColor.white.cgColor
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        return imageView
    }()

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

        frame = CGRect(x: 0, y: 0, width: 50, height: 50)
        addSubview(imageView)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func prepareForDisplay() {
        super.prepareForDisplay()
        if let photoURL = photo {
            let url = URL(string: photoURL)
            imageView.sd_setImage(with: url)
        } else {
            imageView.image = nil
        }
    }
}

【讨论】:

  • 酷,我已经想通了,但另一种方式(你仍然是第一个)。在注释的上下文中不知道 didSet {},谢谢!另外,UIImageView 是否插入边框?
【解决方案2】:

第一个简短的解决方案

好的,经过一番研究,我找到了简短而干净的解决方案。 .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, options: .scaleDownLargeImages, progress: nil, completed: { resultImage,_,_,_,_,_ in
    DispatchQueue.main.async {
      let size = 30
      guard let result = resultImage else { return }
      guard let resized = result.sd_resizedImage(with: CGSize(width: size, height: size), scaleMode: .aspectFit) else { return }
      let modified = resized.transformToMapAnnotation()
      self.image = modified
    }
  })
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    • 2011-01-06
    • 1970-01-01
    相关资源
    最近更新 更多