psharanda / Atributika

Convert text with HTML tags, links, hashtags, mentions into NSAttributedString. Make them clickable with UILabel drop-in replacement.
MIT License
1.45k stars 156 forks source link

Index out of bounds on Add Detection button for strings with emoji and link #77

Closed denniskeithgaso closed 5 years ago

denniskeithgaso commented 5 years ago

I don't know if it's only me. But text with emoji causes a crash on addDetectionAreaButton:

Here's how a similar one was solved 😄 \nhttps://medium.com/@narcelio/solving-decred-mockingbird-puzzle-5366efeaeed7\n
denniskeithgaso commented 5 years ago

I have to add. This particular string causes error on the makeAttributedString method of the AttributedText.swift file on line 58. That's the attributedString.addAttributes(attrs, range: NSRange(d.range, in: string)) function. The xcode error was a fatal error. But it was actually an index out of bounds. The Range (d.range) in the Detection is out of bounds to the attributedString.

The string is:

<p>Today, we are excited to announce our strategic partnership with MOAC, a world-class public blockchain that is among the first to implement sharding to resolve the scalability dilemma. https://medium.com/oathprotocol/oath-protocol-and-moac-team-up-in-strategic-partnership-f38427784a17</p>

My configuration is:

var counter = 0
            var isOrdered = false
            let transformers: [TagTransformer] = [
                TagTransformer.brTransformer,
                TagTransformer(tagName: "ul", tagType: .start) { _ in
                    isOrdered = false
                    return ""
                },
                TagTransformer(tagName: "ol", tagType: .start) { _ in
                    counter = 0
                    isOrdered = true
                    return ""
                },
                TagTransformer(tagName: "li", tagType: .start) { _ in
                    counter += 1
                    return isOrdered ? "\(counter). " : "• "
                },
                TagTransformer(tagName: "li", tagType: .end) { _ in
                    return ""
                }
            ]

            let h1 = Style("h1").font(UIFont(name: "Lato-Bold", size: 34)!)
            let h2 = Style("h2").font(UIFont(name: "Lato-Bold", size: 24)!)
            let h3 = Style("h3").font(UIFont(name: "Lato-Bold", size: 20)!)
            let h4 = Style("h4").font(UIFont(name: "Lato-Bold", size: 19)!)
            let h5 = Style("h5").font(UIFont(name: "Lato-Bold", size: 14)!)
            let h6 = Style("h6").font(UIFont(name: "Lato-Bold", size: 12)!)

            let all = Style.font(fontRegular).foregroundColor(theme.textColor)
            let link = Style.foregroundColor(UIColor.Theme.cryptoBlue, .normal)
                .foregroundColor(.cyan, .highlighted)
            let span = Style("mentioned").foregroundColor(UIColor.Theme.cryptoBlue, .normal)
                .foregroundColor(.blue, .highlighted)
            let a = Style("a").foregroundColor(.orange, .normal)
                .foregroundColor(.cyan, .highlighted)
            let code = Style("code").foregroundColor(.red).font(UIFont.monospacedDigitSystemFont(ofSize: 17, weight: UIFont.Weight.medium))
                .backgroundColor(UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 0.8))
            let pre = Style("pre").font(UIFont.monospacedDigitSystemFont(ofSize: 17, weight: UIFont.Weight.medium))
                .foregroundColor(theme.textColor).backgroundColor(.clear)
            let i = Style("i").obliqueness(0.1)
            let b = Style("b").font(fontBold)
            let strike = Style("strike").strikethroughStyle(.single)
                .strikethroughColor(theme.textColor).baselineOffset(0)
            let em = Style("em").obliqueness(0.3)
            let strong = Style("strong").font(fontBold)
            let edit = Style("edit").font(fontRegular).foregroundColor(.lightGray)
            let highlight = Style("highlight").foregroundColor(UIColor.Theme.cryptoBlue, .normal)
                .foregroundColor(.blue, .highlighted)
            let fa = Style("fa").font(UIFont(name: "FontAwesome", size: 17)!)
                .foregroundColor(theme.textColor)
            let emoji = Style("emoji").font(emojiFont, .normal).font(emojiFont, .highlighted)
                .foregroundColor(.clear, .normal).foregroundColor(.clear, .highlighted)
            let tags = [all, span, a, code, i, b, strike, edit, pre, strong, em, h1, h2, h3, h4, h5, h6, highlight, fa, emoji]

// Where plainView is the AttributedLabel
let str = messageTxt.style(tags: tags, transformers: transformers)
plainView.message.attributedText = str
    .styleLinks(link)
    .styleAll(all)
denniskeithgaso commented 5 years ago

My current quick fix for this is: AttributedText.swift

if d.range.lowerBound >= attributedString.string.startIndex && d.range.upperBound <= attributedString.string.endIndex {
    attributedString.addAttributes(attrs, range: NSRange(d.range, in: string))
}

And for the AttributedLabel.swift on the addDetectionAreaButton:

if detection.range.lowerBound >= inheritedString.string.startIndex && detection.range.upperBound <= inheritedString.string.endIndex {
    let nsrange = NSRange(detection.range, in: text.string)
    layoutManager.enumerateEnclosingRects(forGlyphRange: nsrange, withinSelectedGlyphRange: NSRange(location: NSNotFound, length: 0), in: textContainer, using: { (rect, stop) in
    var finalRect = rect
    finalRect.origin.y += dy
    self.addDetectionAreaButton(frame: finalRect, detection: detection, text: String(text.string[detection.range]))
    })
}

It will simply skip adding attributes.

So here's how it looks like when it happens. Simulator Screen Shot - iPhone 8 Plus - 2019-03-28 at 14 49 48

psharanda commented 5 years ago

Please update to latest version and let me know if it works.

psharanda commented 5 years ago

Cc #76

dankorotin commented 5 years ago

Also crashes with "Мы отправили код на номер\n" and "Подтверждая номер телефона, вы\nпринимаете «<link>пользовательское соглашение</link>»", even after the #76 fix.

psharanda commented 5 years ago

Yeah there was a mаjor issue in parser. It is fixed now. 🎉 Congrats

🚀 Atributika (4.7.2) successfully published 📅 March 28th, 16:50 🌎 https://cocoapods.org/pods/Atributika 👍 Tell your friends!

denniskeithgaso commented 5 years ago

Thank you! It works perfectly! 🎉🎉🎉