iOS中NSAttributedString添加竖图保存后水平翻转的问题
iOS竖版图片添加到NSAttributedString后保存变为横版的解决方法
在iOS开发中,使用NSTextAttachment调整NSAttributedString内的图片大小时,遇到竖版图片保存后变为横版的问题——图片缩放功能正常,但添加到NSAttributedString后出现莫名水平翻转。
问题根源
- 原图片缩放方法使用
UIGraphicsBeginImageContext,未处理图片的imageOrientation属性,导致图片方向信息丢失,绘制后方向异常。 - NSTextAttachment未设置正确的
bounds,仅设置image无法保证图片的显示和存储尺寸与方向匹配。
修改后的代码实现
1. 修复UIImage缩放扩展(处理方向)
extension UIImage { func resizeImage(scale: CGFloat) -> UIImage { let newSize = CGSize(width: size.width * scale, height: size.height * scale) UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } let transform = transformForOrientation(newSize) draw(in: CGRect(origin: .zero, size: newSize).applying(transform)) return UIGraphicsGetImageFromCurrentImageContext()! } private func transformForOrientation(_ newSize: CGSize) -> CGAffineTransform { var transform = CGAffineTransform.identity switch imageOrientation { case .down, .downMirrored: transform = transform.translatedBy(x: newSize.width, y: newSize.height) transform = transform.rotated(by: .pi) case .left, .leftMirrored: transform = transform.translatedBy(x: newSize.width, y: 0) transform = transform.rotated(by: .pi / 2) case .right, .rightMirrored: transform = transform.translatedBy(x: 0, y: newSize.height) transform = transform.rotated(by: -.pi / 2) case .up, .upMirrored: break @unknown default: break } switch imageOrientation { case .upMirrored, .downMirrored: transform = transform.translatedBy(x: newSize.width, y: 0) transform = transform.scaledBy(x: -1, y: 1) case .leftMirrored, .rightMirrored: transform = transform.translatedBy(x: newSize.height, y: 0) transform = transform.scaledBy(x: -1, y: 1) case .up, .down, .left, .right: break @unknown default: break } return transform } }
2. 优化NSAttributedString扩展(设置Attachment Bounds)
extension NSAttributedString { func attributedStringWithResizedImages(with maxWidth: CGFloat) -> NSAttributedString { let text = NSMutableAttributedString(attributedString: self) text.enumerateAttribute(.attachment, in: NSRange(location: 0, length: text.length), options: []) { value, range, stop in guard let attachment = value as? NSTextAttachment else { return } guard let image = attachment.image(forBounds: attachment.bounds, textContainer: NSTextContainer(), characterIndex: range.location) else { return } guard image.size.width > maxWidth else { return } let scale = maxWidth / image.size.width let newImage = image.resizeImage(scale: scale) let newAttachment = NSTextAttachment() newAttachment.image = newImage // 设置正确的bounds,确保图片显示尺寸匹配 newAttachment.bounds = CGRect(origin: .zero, size: newImage.size) text.addAttribute(.attachment, value: newAttachment, range: range) } return text } }
3. 保存与读取逻辑(无需修改)
原保存和读取代码可继续使用,RTFD格式会正确存储设置好的attachment信息:
func saveAttributedTextToUserDefault(attributedText: NSAttributedString, key: String) { do { let data = try attributedText.data(from: NSRange(location: 0, length: attributedText.length), documentAttributes: [.documentType: NSAttributedString.DocumentType.rtfd]) userDefault.set(data, forKeyPath: myNotes) } catch { print(error) } } func getAttributedTextFromUserDefault(key: String) -> NSAttributedString { if let dataValue = userDefault.data(forKey: myNotes) as? Data { do { let attributeText = try NSAttributedString(data: dataValue, documentAttributes: nil) return attributeText } catch { print("error: ", error) } } return NSAttributedString() }
关键修复说明
- 新增
transformForOrientation方法,根据图片的原始方向计算绘制变换,确保缩放后图片方向与原图一致。 - 使用
UIGraphicsBeginImageContextWithOptions替代UIGraphicsBeginImageContext,支持Retina屏幕,避免图片模糊。 - 为新创建的
NSTextAttachment设置bounds,确保图片在AttributedString中的显示尺寸和存储信息准确。
内容的提问来源于stack exchange,提问作者Dian
相关产品推荐
相关产品推荐

