-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed boundingRect(with:options:) miscalculation of MessageLabel like text Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩ #824
Fixed boundingRect(with:options:) miscalculation of MessageLabel like text Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩ #824
Conversation
… text Ꮚ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩
@zhongwuzw I have some concerns about this because 5 extra points is just a magic number. I’ll dig deeper to see what caused this if there’s a bug Sent with GitHawk |
@SD10 Yeah, it's a magic number, I tried |
@zhongwuzw I have seen that before but why did you suggest |
@SD10 Emm, only greater than |
What is the number dependent on though? Does it change based on the length of the string? If I insert 100 of that style of emoticon, will 5pt still be accurate? |
@Sherlouk Good opinion 👍 , but I think it's not just simply based on length, I think we can think it as the biggest margin of error. I don't know wether PS. I test multiple text as you said, and it works currently. |
Yea I mean just in general magic numbers that no one understands is really bad code smell. 5pt might be fine for this specific string or use case, but I sense there will be others. Has a radar been raised explaining this issue? I would say at bare minimum we should make sure that's done and reference it with a comment as well as a link to the discussion so people can get involved and update it later. |
I'm curious if this is a bug in UIKit or MessageKit. I'd like to see what the resulting frame of |
Might be a good idea to try and create a very basic sample app with the sole purpose of checking the bounding box and overlaying a view to see if it's a reasonable size? |
https://github.com/yokochi/MessageKit/tree/test-emoticon-string |
Very basic playground setup, experimented with a few variations (With and without extra text, etc) and the red overlay covered the text every time. Interesting 🤔 I've also just cloned // Edit 1 // Edit 2 |
Okay so I've narrowed down to where it's going wrong, but still need to work out how to actually fix it. This is using the same code, but different texts. I've updated the MessageLabel to show both our way of rendering text but also Apple's. You can see in normal text they're more or less the exact same, therefore MessageKit doesn't have an issue. With the different characters as per this bug, they're not the same. Now the reason it's getting truncated is fairly easy, the textContainer size is too small so it can't fit. Because the lineBreakMode is set to split using wordWrapping this breaks it where it does. Changing this to use byClipping makes it so you can see the whole string but there's less padding on the right vs normal strings. Acceptable for most people, but not amazing. The solution in this PR affects all strings, meaning even working/valid strings will have more padding on the right. An inconsistency I'm not a fan of. We need to find out what's wrong with the way we render text, that means the two solutions don't overlay. Interestingly, the first character seems to be fine - so maybe some weird letter spacing? I don't know - still investigating. |
@Sherlouk Thanks for all that debugging you did. I only have time for a short response -- I'm not a fan of the 5pt magic number because it breaks Strings that are currently being formatted properly. I'm also not a fan of There must be an issue with calculating the text container size when applying the insets: https://github.com/MessageKit/MessageKit/blob/master/Sources/Views/MessageLabel.swift#L172-L182 Fwiw, I would like to move away from |
So yea I narrowed the problem down to the same block of code (as we spoke briefly about on Slack) but the textContainer.size seemed to have no impact on the actual issue at hand in the limited testing I did. That size was actually perfect and as expected. Sure you can add +5 and achieve the same fix, but not what we want. It just seems to be the drawGlyphs call is drawing it slightly different to what happens if you call Haven't tried using these sorts of emoticons in StyledTextKit, but probably worth trying a basic experiment first before going all in. |
I have just a glimpse, not deep in, StyledTextView not provide auto calculation size of On StackOverflow, seems |
I've not looked into either StyledTextView nor that StackOverflow post in detail but I can tell you that the size calculation is working perfectly fine. If you look at the screenshots I posted above, when you let Apple draw the string using our existing size calculations (not with the 5pt hack) it works perfectly fine. It's only when you use our draw method that suddenly it doesn't look right. Sizing isn't the issue here, the drawing is. |
@Sherlouk You can try add the height value after get size of calculation. I think the reason is the width is not sufficient, leads the rest of characters positioned in new line. |
The issue can be in 1 of 3 places IMO: |
@zhongwuzw I think you might be missing what I'm saying though, using the size from Bounding rect is giving the perfect size to show that specific example of text (haven't tried it for all the other possible ones). If you use a plain jane UILabel, set the size of that label to the output of the bounding rect calculation, and set the text. It displays fine. I urge you to try just getting rid of the |
@SD10 @Sherlouk @yokochi Guys, sorry for late reply. I removed
|
return rect.size | ||
|
||
textContainer.size = constraintBox | ||
textStorage.replaceCharacters(in: NSRange(location: 0, length: textStorage.length), with: attributedText) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, we have no thread-safe issue because we do this on main thread. So we can just replace characters.
@zhongwuzw Thanks for looking into this! I can't wait to review it this weekend ❤️ |
@zhongwuzw @SD10 whats the current status of this PR? Should it be included in the 1.1 release? |
@nathantannar4 I think it's ready to ship it, but because it would influence all layout process, so any review back would be more appreciated. 🤔 |
@nathantannar4 As @zhongwuzw this is a pretty big change so I don't think it can make it in the next release. It needs more investigating |
@nathantannar4 Emm, even though step to |
Yeah, as @zhongwuzw said, StyledTextKit won't solve this. The reason I haven't merged this is I'm still not convinced that I really think this issue is related to the draw method and the layout manager in the |
@SD10, Emm, |
private lazy var textContainer: NSTextContainer = { | ||
let textContainer = NSTextContainer() | ||
textContainer.maximumNumberOfLines = 0 | ||
textContainer.lineFragmentPadding = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhongwuzw This is where I left off on reviewing this PR. I was a little concerned that the values used in the textContainer
wouldn't match up with the textContainer
used in MessageLabel
. Let's say that the user changes the MessageLabel
to have a maximumNumberOfLines
to 2 or lineFragmentPadding
to 3. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SD10 Yeah, I also consider this issue, I prefer to let user override size calculation method.But also we can, if user change property of Label
, provide maximunNumberOfLines
lineFragmentPadding
to the layout manager? After that we can calculate specific size for each label? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a good way for the MessageLabel
and the MessageSizeCalculator
to communicate within the framework. I'm really worried that this approach will lead to a large amount of undefined behavior.
We could possibly make a delegate that provides a shared MessageSizeConfiguration
object to both theMessagesCollectionViewFlowLayout
and the MessageLabel
but blahhh 🤢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SD10 Add a shared configuration
between theMessagesCollectionViewFlowLayout
and MessageLabel
seems weird 😂 , I still think users may should override messageContainerSize(for message: MessageType)->CGSize
to return size, and don't change properties of MessageLabel
directly, instead to reload cell. 🤔
Adding a layoutManager and textStorage container adds a penalty in text size calculation over calling EDIT: |
Also see #1136 if it fixes this issue. I've also noticed the insetRect not being used in |
As I posted about on slack, with the release of iOS 13 the internal func labelSize(for attributedText: NSAttributedString, considering maxWidth: CGFloat) -> CGSize {
let constraintBox = CGSize(width: maxWidth, height: .greatestFiniteMagnitude)
/// Workaround:
let adjustedRect: CGRect
if #available(iOS 13, *) {
let fullRange = NSRange(0..<attributedText.length)
var isPartiallyAttributed = false
attributedText.enumerateAttributes(in: fullRange) { value, range, stop in
if !NSEqualRanges(fullRange, range) {
isPartiallyAttributed = true
stop.pointee = true
}
}
var options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
if isPartiallyAttributed {
options.formUnion(NSStringDrawingOptions.usesDeviceMetrics)
}
adjustedRect = attributedText.boundingRect(with: constraintBox,
options: options,
context: nil)
} else {
adjustedRect = attributedText.boundingRect(with: constraintBox,
options: [.usesLineFragmentOrigin, .usesFontLeading],
context: nil)
}
return adjustedRect.size
} If there were already issues with bounding rect, then this provides even more reason to make updates to this logic. I'm seeing a few options:
@marcetcheverry / @zhongwuzw / @SD10 / @nathantannar4 - Any thoughts or input? |
Any update on this one guys? Do we want to start digging into this again? |
So, my above fix causes a separate issue: A single-line, partially attributed string receives the wrong boundingRect. Here is what I think needs to happen:
|
Fixes #812 #816 .
I think it's a bug for
boundingRect(with:options:)
, it would calculates a less size when text likeᏊ˘̴͈́ꈊ˘̴͈̀Ꮚ⋆✩
orTomorrow is ...
.