From e63091c18cd58b837f3dc2bae80f88e882e23a10 Mon Sep 17 00:00:00 2001 From: zhongwuzw Date: Wed, 22 Aug 2018 17:54:48 +0800 Subject: [PATCH 1/3] =?UTF-8?q?Fixed=20boundingRect(with:options:)=20misca?= =?UTF-8?q?lculation=20of=20MessageLabel=20like=20text=20=E1=8F=8A=CB=98?= =?UTF-8?q?=CC=B4=CD=88=CC=81=EA=88=8A=CB=98=CC=B4=CD=88=CC=80=E1=8F=8A?= =?UTF-8?q?=E2=8B=86=E2=9C=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++++ Sources/Layout/MessageSizeCalculator.swift | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd7961a..1195ab39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/Messa ## Upcoming release +### Fixed + +- Fixed `boundingRect(with:options:)` miscalculation of `MessageLabel` , like text `Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩`、`Tomorrow is the day`. +[#824](https://github.com/MessageKit/MessageKit/pull/824) by [@zhongwuzw](https://github.com/zhongwuzw). + ### Changed - The `MessageData.emoji` case once again uses a default font of 2x the `messageLabelFont` size. diff --git a/Sources/Layout/MessageSizeCalculator.swift b/Sources/Layout/MessageSizeCalculator.swift index 7e563629..ef7a5ae3 100644 --- a/Sources/Layout/MessageSizeCalculator.swift +++ b/Sources/Layout/MessageSizeCalculator.swift @@ -24,6 +24,8 @@ import Foundation +private let additionalWidthForBoundingRectCalculation: CGFloat = 5.0 + open class MessageSizeCalculator: CellSizeCalculator { public init(layout: MessagesCollectionViewFlowLayout? = nil) { @@ -216,8 +218,9 @@ open class MessageSizeCalculator: CellSizeCalculator { internal func labelSize(for attributedText: NSAttributedString, considering maxWidth: CGFloat) -> CGSize { let constraintBox = CGSize(width: maxWidth, height: .greatestFiniteMagnitude) let rect = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).integral - - return rect.size + + // `boundingRect` method maybe not calculate correctly, like `Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩`, so we add 5 points to fix them temporary. + return CGSize(width: rect.width + additionalWidthForBoundingRectCalculation, height: rect.height) } } From 99c9e3f26749cb40869303450c20b155fbd32417 Mon Sep 17 00:00:00 2001 From: zhongwuzw Date: Fri, 7 Sep 2018 15:52:12 +0800 Subject: [PATCH 2/3] Using NSLayoutManager to calculate size --- CHANGELOG.md | 2 +- Sources/Layout/MessageSizeCalculator.swift | 29 ++++++++++++++++++---- Sources/Views/MessageLabel.swift | 2 +- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1195ab39..882c5795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/Messa ### Fixed -- Fixed `boundingRect(with:options:)` miscalculation of `MessageLabel` , like text `Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩`、`Tomorrow is the day`. +- Fixed `boundingRect(with:options:)` miscalculation of `MessageLabel` by using `NSLayoutManager`, like text `Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩`、`Tomorrow is the day`. [#824](https://github.com/MessageKit/MessageKit/pull/824) by [@zhongwuzw](https://github.com/zhongwuzw). ### Changed diff --git a/Sources/Layout/MessageSizeCalculator.swift b/Sources/Layout/MessageSizeCalculator.swift index ef7a5ae3..af8c9233 100644 --- a/Sources/Layout/MessageSizeCalculator.swift +++ b/Sources/Layout/MessageSizeCalculator.swift @@ -24,8 +24,6 @@ import Foundation -private let additionalWidthForBoundingRectCalculation: CGFloat = 5.0 - open class MessageSizeCalculator: CellSizeCalculator { public init(layout: MessagesCollectionViewFlowLayout? = nil) { @@ -51,6 +49,23 @@ open class MessageSizeCalculator: CellSizeCalculator { public var incomingMessageBottomLabelAlignment = LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 42)) public var outgoingMessageBottomLabelAlignment = LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 42)) + + private lazy var textContainer: NSTextContainer = { + let textContainer = NSTextContainer() + textContainer.maximumNumberOfLines = 0 + textContainer.lineFragmentPadding = 0 + return textContainer + }() + private lazy var layoutManager: NSLayoutManager = { + let layoutManager = NSLayoutManager() + layoutManager.addTextContainer(textContainer) + return layoutManager + }() + private lazy var textStorage: NSTextStorage = { + let textStorage = NSTextStorage() + textStorage.addLayoutManager(layoutManager) + return textStorage + }() open override func configure(attributes: UICollectionViewLayoutAttributes) { guard let attributes = attributes as? MessagesCollectionViewLayoutAttributes else { return } @@ -217,10 +232,14 @@ open class MessageSizeCalculator: CellSizeCalculator { internal func labelSize(for attributedText: NSAttributedString, considering maxWidth: CGFloat) -> CGSize { let constraintBox = CGSize(width: maxWidth, height: .greatestFiniteMagnitude) - let rect = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).integral - // `boundingRect` method maybe not calculate correctly, like `Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩`, so we add 5 points to fix them temporary. - return CGSize(width: rect.width + additionalWidthForBoundingRectCalculation, height: rect.height) + textContainer.size = constraintBox + textStorage.replaceCharacters(in: NSRange(location: 0, length: textStorage.length), with: attributedText) + layoutManager.ensureLayout(for: textContainer) + + let size = layoutManager.usedRect(for: textContainer).size + + return CGSize(width: size.width.rounded(.up), height: size.height.rounded(.up)) } } diff --git a/Sources/Views/MessageLabel.swift b/Sources/Views/MessageLabel.swift index 7270e805..60636833 100644 --- a/Sources/Views/MessageLabel.swift +++ b/Sources/Views/MessageLabel.swift @@ -172,7 +172,7 @@ open class MessageLabel: UILabel { open override func drawText(in rect: CGRect) { let insetRect = UIEdgeInsetsInsetRect(rect, textInsets) - textContainer.size = CGSize(width: insetRect.width, height: rect.height) + textContainer.size = CGSize(width: insetRect.width, height: insetRect.height) let origin = insetRect.origin let range = layoutManager.glyphRange(for: textContainer) From 6bb85867775e12133a8014a4107045b6f9052c4b Mon Sep 17 00:00:00 2001 From: Roman Podymov Date: Fri, 20 Oct 2023 17:45:51 +0200 Subject: [PATCH 3/3] From yokochi/MessageKit/pull/1 --- Sources/Layout/MessageSizeCalculator.swift | 28 ++++++++++++++++++---- Sources/Views/MessageLabel.swift | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Sources/Layout/MessageSizeCalculator.swift b/Sources/Layout/MessageSizeCalculator.swift index 1d9065c9..09948721 100644 --- a/Sources/Layout/MessageSizeCalculator.swift +++ b/Sources/Layout/MessageSizeCalculator.swift @@ -316,15 +316,33 @@ open class MessageSizeCalculator: CellSizeCalculator { } // MARK: Internal + internal lazy var textContainer: NSTextContainer = { + let textContainer = NSTextContainer() + textContainer.maximumNumberOfLines = 0 + textContainer.lineFragmentPadding = 0 + return textContainer + }() + internal lazy var layoutManager: NSLayoutManager = { + let layoutManager = NSLayoutManager() + layoutManager.addTextContainer(textContainer) + return layoutManager + }() + internal lazy var textStorage: NSTextStorage = { + let textStorage = NSTextStorage() + textStorage.addLayoutManager(layoutManager) + return textStorage + }() internal func labelSize(for attributedText: NSAttributedString, considering maxWidth: CGFloat) -> CGSize { let constraintBox = CGSize(width: maxWidth, height: .greatestFiniteMagnitude) - let rect = attributedText.boundingRect( - with: constraintBox, - options: [.usesLineFragmentOrigin, .usesFontLeading], - context: nil).integral - return rect.size + textContainer.size = constraintBox + textStorage.replaceCharacters(in: NSRange(location: 0, length: textStorage.length), with: attributedText) + layoutManager.ensureLayout(for: textContainer) + + let size = layoutManager.usedRect(for: textContainer).size + + return CGSize(width: size.width.rounded(.up), height: size.height.rounded(.up)) } } diff --git a/Sources/Views/MessageLabel.swift b/Sources/Views/MessageLabel.swift index feb9c95a..91f388a8 100644 --- a/Sources/Views/MessageLabel.swift +++ b/Sources/Views/MessageLabel.swift @@ -130,7 +130,7 @@ open class MessageLabel: UILabel { open override func drawText(in rect: CGRect) { let insetRect = rect.inset(by: textInsets) - textContainer.size = CGSize(width: insetRect.width, height: rect.height) + textContainer.size = CGSize(width: insetRect.width, height: insetRect.height) let origin = insetRect.origin let range = layoutManager.glyphRange(for: textContainer)