UILabe 图文混排展示gif emoji表情

github地址

适用需求

在UILabel中展示自定义emoji或者gif/apng动画emoji,类似QQ聊天效果

背景介绍

使用NSAttributeStringNSTextAttachment可以在UILabel中进行图文混排,因此可以把一张emoji图片设置为NSTextAttachment.image或者.data

let testAttachment = NSTextAttachment()
testAttachment.image = UIImage(named: <#T##String#>)
let testAttributeString = NSAttributedString(attachment:testAttachment)
let testLabel = UILabel()
testLabel.attributedText = testAttributeString

但是这个方法不能展示gif等序列图动画,因此我去研究了一下大名鼎鼎的YYText,bingo!完美解决!!!而且对YYText的强大功能和简洁的代码崇拜不已。But,杀鸡焉用牛刀。因此,在参考YYText的基础上,我自己写了一个满足需求的简易的EmojiLabel

正文

  1. 从gif里面获取每一帧图片
    private func gifInfo(data: Data) -> [GifFrame] {
        let source = CGImageSourceCreateWithData(data as CFData, nil)
        let count = CGImageSourceGetCount(source!)
        if count == 1 {
            if let image = UIImage(data: data) {
                return [GifFrame(image: image, delayTime: 0)]
            } else {
                return [GifFrame(image: UIImage(), delayTime: 0)]
            }
        }
        var gifFrames = [GifFrame]()
        for i in 0..
  2. 怎么让图片动起来?

    前面已经说过,使用NSTextAttachment可以展示图片并且可以从gif序列里面把每一帧图片取出来,再加个定时器来不断的刷新图片
    Swift tick = CADisplayLink(target: _AnimatedAttachmentWeakProxy(target: self), selector: #selector(step)) tick.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
    Swift @objc private func step() { if index >= gifFrames.count { index = 0 } let curDelayTime = gifFrames[index].delayTime time += tick.duration self.image = gifFrames[index].image if time >= TimeInterval(curDelayTime) { time = 0 index += 1 } display?() }
    Swift attachment.display = { [weak self] in self?.setNeedsDisplay() }
    Swift override public var attributedText: NSAttributedString? { didSet { if let attributedText = attributedText { attributedText.enumerateAttribute(.attachment, in: NSMakeRange(0, attributedText.length), options: .reverse) { (obj, range, stop) in if let attachment = obj as? AnimatedAttachment { attachment.display = { [weak self] in self?.setNeedsDisplay() } } } } } }
    前两段代码是在AnimatedAttachment里面加一个定时器来遍历gif序列并且回调AttachmentLabel;第三段是AttachmentLabel中刷新;第四段是当AttachmentLabelattributedText变化时向AnimatedAttachment注册回调

  3. 解析收到的内容 EmoticonParser
    @objc
    func parser(text: String) -> NSAttributedString? {
        let attributeString = NSMutableAttributedString(string: text)
        do {
            var flags = [String]()
            for item in items {
                flags.append(item.flag)
            }
            if flags.isEmpty == true {
                return attributeString
            }
            let pattern = flags.joined(separator: "|")
            let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
            var results = [NSTextCheckingResult]()
            regex.enumerateMatches(in: text, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, text.count)) { (result, flag, stop) in
                guard let result = result else { return }
                results.append(result)
            }
            
            if results.isEmpty == true {
                return attributeString
            }
            
            for result in results.reversed() {
                autoreleasepool {
                    let fromIndex = text.index(text.startIndex, offsetBy: result.range.lowerBound)
                    let toIndex = text.index(text.startIndex, offsetBy: result.range.lowerBound + result.range.length)
                    let attachmentFlagString = text[fromIndex..
    在这个过程中,使用正则表达式来获取text中的表情特殊符,注意:for 循环要倒叙进行否则会出现越界的问题

使用

整个的使用过程是这样的

let path = Bundle.main.path(forResource: "[email protected]", ofType: nil)
let data = try! Data.init(contentsOf: URL(fileURLWithPath: path!)
let path2 = Bundle.main.path(forResource: "[email protected]", ofType: nil)
let data2 = try! Data.init(contentsOf: URL(fileURLWithPath: path2!)
let path4 = Bundle.main.path(forResource: "[email protected]"ofType: nil)
let data4 = try! Data.init(contentsOf: URL(fileURLWithPath: path4!)
let item1 = EmoticonItem(flag: ":0:", emoticon: data, size: CGSi(width: 50, height: 50))
let item2 = EmoticonItem(flag: ":1:", emoticon: data2)
let item4 = EmoticonItem(flag: ":3:", emoticon: data4)
let parser = EmoticonParser(items: [item1, item2, item4]
let label = AttachmentLabel(frame: CGRect(x: 30, y: 30, width: 200height: 200))
label.backgroundColor = .gray
label.numberOfLines = 0
label.lineBreakMode = .byCharWrapping
view.addSubview(label
label.parser = parser
label.text = "是的护发素电:0:话费是的咖啡机啊但是jskdfja独立开:1:发绝对是计费可拉伸的就开放"
label.textColor = .white

github地址

我的个人博客
欢迎访问www.iosprogrammer.tech

谢谢!!!

你可能感兴趣的:(UILabe 图文混排展示gif emoji表情)