Tusker/Tusker/Views/EmojiLabel.swift

104 lines
3.8 KiB
Swift

//
// EmojiLabel.swift
// Tusker
//
// Created by Shadowfacts on 3/1/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
class EmojiLabel: UILabel {
private var emojiIdentifier: String?
private var emojiRequests: [ImageCache.Request] = []
func setEmojis(_ emojis: [Emoji], identifier: String) {
guard emojis.count > 0, let attributedText = attributedText else { return }
self.emojiIdentifier = identifier
emojiRequests.forEach { $0.cancel() }
emojiRequests = []
let matches = emojiRegex.matches(in: attributedText.string, options: [], range: attributedText.fullRange)
let emojiImages = CachedDictionary<UIImage>(name: "EmojiLabel Emoji Images")
let group = DispatchGroup()
for emoji in emojis {
// only make requests for emojis that are present in the text to avoid making unnecessary network requests
guard matches.contains(where: { (match) in
let matchShortcode = (attributedText.string as NSString).substring(with: match.range(at: 1))
return emoji.shortcode == matchShortcode
}) else {
continue
}
group.enter()
let request = ImageCache.emojis.get(emoji.url) { (data) in
defer { group.leave() }
guard let data = data, let image = UIImage(data: data) else {
return
}
emojiImages[emoji.shortcode] = image
}
if let request = request {
emojiRequests.append(request)
}
}
group.notify(queue: .main) { [weak self] in
// if e.g. the account changes before all emojis are loaded, don't bother trying to set them
guard let self = self, self.emojiIdentifier == identifier else { return }
let mutAttrString = NSMutableAttributedString(attributedString: attributedText)
// replaces the emojis starting from the end of the string as to not alter the indicies of preceeding emojis
for match in matches.reversed() {
let shortcode = (mutAttrString.string as NSString).substring(with: match.range(at: 1))
guard let emojiImage = emojiImages[shortcode] else {
continue
}
let attachment = NSTextAttachment(emojiImage: emojiImage, in: self.font, with: self.textColor)
let attachmentStr = NSAttributedString(attachment: attachment)
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
}
self.attributedText = mutAttrString
self.setNeedsLayout()
self.setNeedsDisplay()
}
}
func removeEmojis() {
emojiIdentifier = nil
emojiRequests.forEach { $0.cancel() }
emojiRequests = []
}
}
extension EmojiLabel {
func updateForAccountDisplayName(account: Account) {
if Preferences.shared.hideCustomEmojiInUsernames {
self.text = account.displayName
self.removeEmojis()
} else {
self.text = account.displayName
self.setEmojis(account.emojis, identifier: account.id)
}
}
func updateForAccountDisplayName(account: AccountMO) {
if Preferences.shared.hideCustomEmojiInUsernames {
self.text = account.displayNameWithoutCustomEmoji
self.removeEmojis()
} else {
self.text = account.displayOrUserName
self.setEmojis(account.emojis, identifier: account.id)
}
}
}