Swift scaleAspectFit图像作为远程NSTextAttachment

xriantvc  于 2022-12-17  发布在  Swift
关注(0)|答案(1)|浏览(123)

问题:

我需要一个contentMode.scaleAspectFit类属性的图像用作NSTextAttachment。
附件图像在预设的displaySize中显示不佳。

重复:

我这样设置displaySize,但是我需要使图像的外观适合附件。

attachment = RemoteImageTextAttachment(imageURL: fileUrl!, displaySize: CGSize(width: 300, height: 300))

我需要复制下面的函数,但要修改它以适合附件图像的纵横比,从而适合设置的displaySize内的所有高度和宽度图像。

public class RemoteImageTextAttachment: NSTextAttachment {
  
  // The label that this attachment is being added to
  public weak var label: UILabel?
  
  // The size to display the image. If nil, the image's size will be used
  public var displaySize: CGSize?
  
  public var downloadQueue: DispatchQueue?
  public let imageUrl: URL
  
  private weak var textContainer: NSTextContainer?
  private var isDownloading = false
    
    var imageCache = AutoPurgingImageCache()
  
  public init(imageURL: URL, displaySize: CGSize? = nil, downloadQueue: DispatchQueue? = nil) {
    self.imageUrl = imageURL
    self.displaySize = displaySize
    self.downloadQueue = downloadQueue
    super.init(data: nil, ofType: nil)
  }
  
  required init?(coder: NSCoder) {
    fatalError()
  }
  
  override public func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
    
    if let displaySize = displaySize {
      return CGRect(origin: .zero, size: displaySize)
    }
    
    if let originalImageSize = image?.size {
      return CGRect(origin: .zero, size: originalImageSize)
    }
    
    // If we return .zero, the image(forBounds:textContainer:characterIndex:) function won't be called
    let placeholderSize = CGSize(width: 1, height: 1)
    return CGRect(origin: .zero, size: placeholderSize)
  }
  
  override public func image(forBounds imageBounds: CGRect, textContainer: NSTextContainer?, characterIndex charIndex: Int) -> UIImage? {
    
    if let image = image {
      return image
    }
    
    self.textContainer = textContainer
    
    guard !isDownloading else {
      return nil
    }
    
    isDownloading = true
    
    let imageUrl = self.imageUrl
    let downloadQueue = self.downloadQueue ?? DispatchQueue.global()
    downloadQueue.async { [weak self] in
      let data = try? Data(contentsOf: imageUrl)
      DispatchQueue.main.async { [weak textContainer] in
        guard let strongSelf = self else {
          return
        }
        
        defer {
          strongSelf.isDownloading = false
        }
        
        guard let data = data else {
          return
        }
        

        strongSelf.image = UIImage(data: data)
        strongSelf.label?.setNeedsDisplay()
        
        // For UITextView/NSTextView
        if let layoutManager = self?.textContainer?.layoutManager,
          let ranges = layoutManager.rangesForAttachment(strongSelf) {
          ranges.forEach { range in
            layoutManager.invalidateLayout(forCharacterRange: range, actualCharacterRange: nil)
          }
        }
      }
    }
    
    return nil
  }
}

public extension NSLayoutManager {
  func rangesForAttachment(_ attachment: NSTextAttachment) -> [NSRange]? {
    guard let textStorage = textStorage else {
      return nil
    }
    var ranges: [NSRange] = []
    textStorage.enumerateAttribute(.attachment, in: NSRange(location: 0, length: textStorage.length), options: [], using: { (attribute, range, _) in
      
      if let foundAttribute = attribute as? NSTextAttachment, foundAttribute === attachment {
        ranges.append(range)
      }
    })
    
    return ranges
  }
}

伪代码:

if let displaySize = displaySize {
          displaySize.contentMode = .scaleAspectFit
          return CGRect(origin: .zero, size: displaySize)
        }
e5njpo68

e5njpo681#

我找到了一种方法,可以获取原始图像的大小,并调整原始图像的大小以适合任何目标CGSize或容器。

if let originalImageSize = image?.size {
    
    let targetSize = CGSize(width: 300, height: 300)
    
    // Compute the scaling ratio for the width and height separately
    let widthScaleRatio = targetSize.width / originalImageSize.width
    let heightScaleRatio = targetSize.height / originalImageSize.height

    // To keep the aspect ratio, scale by the smaller scaling ratio
    let scaleFactor = min(widthScaleRatio, heightScaleRatio)

    // Multiply the original image’s dimensions by the scale factor
    // to determine the scaled image size that preserves aspect ratio
    let scaledImageSize = CGSize(
        width: originalImageSize.width * scaleFactor,
        height: originalImageSize.height * scaleFactor)
    
    return CGRect(origin: .zero, size: scaledImageSize)
}

Source - Resize UIImage Without Stretching in Swift

相关问题