From 97a95c435e788765417499badafa664ba7ccf63d Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 5 Nov 2022 11:00:14 -0400 Subject: [PATCH] Improve performance when displaying posts with many custom emojis Closes #204 --- .../Extensions/NSTextAttachment+Emoji.swift | 6 +-- Tusker/Views/BaseEmojiLabel.swift | 37 ++++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/Tusker/Extensions/NSTextAttachment+Emoji.swift b/Tusker/Extensions/NSTextAttachment+Emoji.swift index 36bbe187..ec056e7b 100644 --- a/Tusker/Extensions/NSTextAttachment+Emoji.swift +++ b/Tusker/Extensions/NSTextAttachment+Emoji.swift @@ -22,8 +22,7 @@ extension NSTextAttachment { image.draw(in: CGRect(origin: .zero, size: imageSizeMatchingFontSize)) } - self.init() - self.image = attachmentImage + self.init(image: attachmentImage) } convenience init(emojiPlaceholderIn font: UIFont) { @@ -31,7 +30,6 @@ extension NSTextAttachment { // assumes emoji are mostly square let size = CGSize(width: adjustedCapHeight, height: adjustedCapHeight) let image = UIGraphicsImageRenderer(size: size).image { (_) in } - self.init() - self.image = image + self.init(image: image) } } diff --git a/Tusker/Views/BaseEmojiLabel.swift b/Tusker/Views/BaseEmojiLabel.swift index 2ceb237e..9b706075 100644 --- a/Tusker/Views/BaseEmojiLabel.swift +++ b/Tusker/Views/BaseEmojiLabel.swift @@ -86,28 +86,31 @@ extension BaseEmojiLabel { func buildStringWithEmojisReplaced(usePlaceholders: Bool) -> NSAttributedString { let mutAttrString = NSMutableAttributedString(attributedString: attributedString) - // lock once for the entire loop, rather than lock/unlocking for each iteration to do the lookup // OSAllocatedUnfairLock.withLock expects a @Sendable closure, so this warns about captures of non-sendable types (attribute dstrings, text checking results) // even though the closures is invoked on the same thread that withLock is called, so it's unclear why it needs to be @Sendable (FB11494878) // so, just ignore the warnings - emojiImages.withLock { emojiImages in - // replaces the emojis starting from the end of the string as to not alter the indices of preceeding emojis - for match in matches.reversed() { - let shortcode = (attributedString.string as NSString).substring(with: match.range(at: 1)) - let attachment: NSTextAttachment - - if let emojiImage = emojiImages[shortcode] { - attachment = NSTextAttachment(emojiImage: emojiImage, in: self.emojiFont, with: self.emojiTextColor) - } else if usePlaceholders { - attachment = NSTextAttachment(emojiPlaceholderIn: self.emojiFont) - } else { - continue - } - - let attachmentStr = NSAttributedString(attachment: attachment) - mutAttrString.replaceCharacters(in: match.range, with: attachmentStr) + let emojiAttachments = emojiImages.withLock { + $0.mapValues { image in + NSTextAttachment(emojiImage: image, in: self.emojiFont, with: self.emojiTextColor) } } + let placeholder = usePlaceholders ? NSTextAttachment(emojiPlaceholderIn: self.emojiFont) : nil + + // replaces the emojis starting from the end of the string as to not alter the indices of preceeding emojis + for match in matches.reversed() { + let shortcode = (attributedString.string as NSString).substring(with: match.range(at: 1)) + let attachment: NSTextAttachment + if let emoji = emojiAttachments[shortcode] { + attachment = emoji + } else if usePlaceholders { + attachment = placeholder! + } else { + continue + } + + let attachmentStr = NSAttributedString(attachment: attachment) + mutAttrString.replaceCharacters(in: match.range, with: attachmentStr) + } return mutAttrString }