macOS中NSImage的textToImage方法返回结果不一致问题
问题:图片添加文字后缩放效果不稳定
我用以下NSImage扩展给图片叠加文字,调用时先生成带文字的图片,再缩小尺寸用于Widget(避免超过16MB限制),但有时候生成的图片效果异常——文字变得模糊错位,看起来像是缩放操作提前执行了一样。
正常效果与异常效果对比:

用于添加文字的NSImage扩展代码
extension NSImage { //https://stackoverflow.com/questions/54450429/macos-add-a-text-overlay-to-an-image-in-swift func textToImage(naam: String, slogan: String) -> NSImage { let targetImage = NSImage(size: self.size, flipped: false) { (dstRect: CGRect) -> Bool in let naamColor = NSColor.black let naamFont = NSFont(name: "Montserrat-SemiBold", size: 110)! let paragraph = NSMutableParagraphStyle() paragraph.alignment = .center let naamFontAttributes = [NSAttributedString.Key.font: naamFont, .paragraphStyle: paragraph, NSAttributedString.Key.foregroundColor: naamColor] as [NSAttributedString.Key : Any] let naamString = NSAttributedString(string: naam, attributes: naamFontAttributes) self.draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)) let rectNaam = CGRect(x: 25, y: -25, width: self.size.width-25, height: self.size.height) naamString.draw(in: rectNaam) return true } return targetImage } }
调用代码
let image = loadImage(named: "eigenradiostation.png") let imageWithText = image.textToImage(naam: radiostationGeselecteerd[0], slogan: radiostationGeselecteerd[1]) //downsample zodat widget niet > 16 MB komt imageWithText.size = CGSize(width: 100, height: 100) let imageData = imageWithText.tiffRepresentation! recentAfgespeeldeRadiostationsTegels.insert(imageData, at:0)
问题原因
问题出在NSImage的延迟绘制机制上:你创建targetImage时用了闭包绘制的方式,这个闭包并不会在调用textToImage时立即执行,而是要等到图片实际需要被渲染(比如调用tiffRepresentation获取数据)的时候才会触发。
而你先修改了imageWithText.size,这会导致闭包执行时,self.size(也就是原图片的尺寸)已经被改成了缩小后的100x100,文字会基于这个小尺寸绘制,最终生成的图片再被导出,就会出现文字模糊错位的异常效果。
解决方法
方法一:先导出图片数据再缩放
确保闭包在尺寸修改前执行完毕,再对导出的图片进行缩小:
let image = loadImage(named: "eigenradiostation.png") let imageWithText = image.textToImage(naam: radiostationGeselecteerd[0], slogan: radiostationGeselecteerd[1]) // 先获取完整尺寸的图片数据,触发闭包绘制 guard let originalData = imageWithText.tiffRepresentation else { return } // 从数据重新生成图片并缩小尺寸 if let downsampledImage = NSImage(data: originalData) { downsampledImage.size = CGSize(width: 100, height: 100) guard let downsampledData = downsampledImage.tiffRepresentation else { return } recentAfgespeeldeRadiostationsTegels.insert(downsampledData, at:0) }
方法二:固定原图片尺寸,避免受后续修改影响
修改textToImage方法,提前保存原图片的尺寸,闭包内使用这个固定值绘制,不受后续size修改的影响:
extension NSImage { func textToImage(naam: String, slogan: String) -> NSImage { // 提前保存原图片的尺寸,避免后续修改干扰绘制 let originalSize = self.size let targetImage = NSImage(size: originalSize, flipped: false) { (dstRect: CGRect) -> Bool in let naamColor = NSColor.black let naamFont = NSFont(name: "Montserrat-SemiBold", size: 110)! let paragraph = NSMutableParagraphStyle() paragraph.alignment = .center let naamFontAttributes = [ NSAttributedString.Key.font: naamFont, .paragraphStyle: paragraph, NSAttributedString.Key.foregroundColor: naamColor ] as [NSAttributedString.Key : Any] let naamString = NSAttributedString(string: naam, attributes: naamFontAttributes) // 使用固定的原尺寸绘制原图 self.draw(in: CGRect(origin: .zero, size: originalSize)) // 基于原尺寸计算文字位置 let rectNaam = CGRect(x: 25, y: -25, width: originalSize.width - 25, height: originalSize.height) naamString.draw(in: rectNaam) return true } return targetImage } }
内容的提问来源于stack exchange,提问作者Patrick Koning
相关产品推荐
相关产品推荐

