Open noox89 opened 9 months ago
I will take a look into this but if feels strange. Can you print attributes of the selected string, in order to distinguish, what is the issue (if its kerning or paragraph style or something else...) Thanks!
Here is the print of the attributed string. The unexpected behaviour only happens with emoticon at the beginning of the line
NSColor = "<UIDynamicCatalogSystemColor: 0x132410540; name = labelColor>";
NSFont = "<UICTFont: 0x131db6080> font-family: \".SFUI-Semibold\"; font-weight: bold; font-style: normal; font-size: 16.00pt";
NSParagraphStyle = "Alignment Natural, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode WordWrapping, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection LeftToRight, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
}😊{
NSColor = "<UIDynamicCatalogSystemColor: 0x132410540; name = labelColor>";
NSFont = "<UICTFont: 0x13b639800> font-family: \".AppleColorEmojiUI\"; font-weight: normal; font-style: normal; font-size: 16.00pt";
NSOriginalFont = "<UICTFont: 0x131db6080> font-family: \".SFUI-Semibold\"; font-weight: bold; font-style: normal; font-size: 16.00pt";
NSParagraphStyle = "Alignment Natural, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode WordWrapping, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection LeftToRight, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
}this is a test{
NSColor = "<UIDynamicCatalogSystemColor: 0x132410540; name = labelColor>";
NSFont = "<UICTFont: 0x131db6080> font-family: \".SFUI-Semibold\"; font-weight: bold; font-style: normal; font-size: 16.00pt";
NSParagraphStyle = "Alignment Natural, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode WordWrapping, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection LeftToRight, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
}
Hello @noox89 again,
I think I found the issue but I am not quite sure what to do with it right now.
You see, when apple renders Emojis, it uses font Apple Color Emoji which is special font with very nice symbolic traits which are probably not available in UIKit and we need to fix this in CoreText.
The issue is not just one function, altought I would be happy to fix it only in that way...
Given the font:
The issue is in here:
func setRichTextStyle(
_ style: RichTextStyle,
to newValue: Bool
) {
let value = newValue ? 1 : 0
switch style {
case .bold, .italic:
let styles = richTextStyles
guard styles.shouldAddOrRemove(style, newValue) else { return }
>>>>>>>>>>>> guard let font = richTextFont else { return } <<<<<<<<<<<<<<<
guard let newFont = font.toggling(style) else { return }
setRichTextFont(newFont)
case .underlined:
setRichTextAttribute(.underlineStyle, to: value)
case .strikethrough:
setRichTextAttribute(.strikethroughStyle, to: value)
}
}
which when we apply font style, we get richTextFont, our poor NSAttributes cannot get all the fonts at all ranges, so they return just the first font, which (unlucky for you) is Apple Color Emoji It sets this font to whole text together with bold and thats where the troubles begin. Because this font should be used for emojis only, it has some quirks like super long spaces etc and it causes the behaviour you are seeing.
I See the solution in some steps:
cc @danielsaidi looks like we have serious issue 😨
https://github.com/danielsaidi/RichTextKit/assets/17381941/338cb2cd-8d47-4b01-bbf9-edd80feb8049
BTW Thanks for reporting this❗️
Looks like my little PoC fixes the issue, but I need to add unit tests probably for this afterwards...
https://github.com/danielsaidi/RichTextKit/assets/17381941/64f6ff5f-bd65-489f-8300-3447937a30e7
func setRichTextStyle(
_ style: RichTextStyle,
to newValue: Bool
) {
let value = newValue ? 1 : 0
switch style {
case .bold, .italic:
carymaryfuk😅()
// let styles = richTextStyles
// guard styles.shouldAddOrRemove(style, newValue) else { return }
// guard let font = richTextFont else { return }
// guard let newFont = font.toggling(style) else { return }
// setRichTextFont(newFont)
case .underlined:
setRichTextAttribute(.underlineStyle, to: value)
case .strikethrough:
setRichTextAttribute(.strikethroughStyle, to: value)
}
}
func carymaryfuk😅() {
var updatedAttributes: [NSAttributedString.Key: Any] = [:]
richText.enumerateAttributes(in: selectedRange, options: [.longestEffectiveRangeNotRequired]) { (attributes, range, _) in
var updatedFont: UIFont?
if let existingFont = attributes[.font] as? UIFont {
print(existingFont)
let newFontDescriptor = existingFont.fontDescriptor
var traits = existingFont.fontDescriptor.symbolicTraits
// Check and modify bold trait
if existingFont.isBold {
traits.remove(.traitBold)
} else {
traits.insert(.traitBold)
}
// Check and modify italic trait
if existingFont.isItalic {
traits.remove(.traitItalic)
} else {
traits.insert(.traitItalic)
}
if let newFontDescriptor = newFontDescriptor.withSymbolicTraits(traits) {
updatedFont = UIFont(descriptor: newFontDescriptor, size: existingFont.pointSize)
}
updatedAttributes[.font] = updatedFont
richText.enumerateAttributes(in: range, options: []) { (attributes, range, _) in
if let font = attributes[.font] as? UIFont {
updatedAttributes[.font] = updatedFont ?? font
}
}
let updatedAttributedString = NSAttributedString(string: richText.attributedSubstring(from: range).string, attributes: updatedAttributes)
(self as? RichTextView)?.textStorage.replaceCharacters(in: range, with: updatedAttributedString)
}
}
}
.....
// Little helpers
extension UIFont {
var isBold: Bool {
return fontDescriptor.symbolicTraits.contains(.traitBold)
}
var isItalic: Bool {
return fontDescriptor.symbolicTraits.contains(.traitItalic)
}
}
You can see I am enumerating the attributes and setting each coresponding font to just bold or italic (with some sample extensions)
I am going to sleep now, but I guess I can take a look tommorow :)
Thanks and good night :)
Thanks for explanation and quick turnaround!:) Btw I like your new func -> čáry máry fuk 🧙♂️
Díky/Thanks🥳
Omg, great find @noox89 and @DominikBucher12
Let's release 1.0 and look at this later.
https://github.com/danielsaidi/RichTextKit/assets/39130272/92d5d6b5-8b36-4a22-95ce-526c9787dd5c
When selecting text which includes emoticons and applying
context.toggleStyle(.bold)
orcontext.toggleStyle(.italic)
the spacing gets disrupted andtoggleStyle
stops working. To get back to normal, font needs to be changed.