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 155 forks source link

[Img] When I add an image, the onClick doesn't work #121

Open jordizspmobile opened 4 years ago

jordizspmobile commented 4 years ago

Hello all!

First at all, I think that library is amazing, but I have an issue with the library, this issue is produced when:

I add an image with <img scr="http://www.example.com/image.jpg" /> all the links with 'a' tags don't work because onClick doesn't call.

Is there any issue or I do something wrong?

My code:

private var bodyTextView = AttributedLabel()

bodyTextView.numberOfLines = 0
bodyTextView.onClick = { label, detection in
     switch detection.type {
           case .link(let url):
                  UIApplication.shared.open(url)
                  break
           case .tag(let tag):
                   if let urlStr = tag.attributes["href"],
                       let url = URL(string: urlStr) {
                       UIApplication.shared.open(url)
                   }
                   break
            default:
                   break
      }
}

let string = "Example text <a href='http://www.example.com/examplelink'>Example link</a><br /><br /><img src='http://www.example.com/image.jpg' />"
let bodyHTML = string.toHtml([
   StyleParams.paragraphFont: UIFont.systemFont(ofSize: 15, weight: .regular),
   StyleParams.paragraphColor: UIColor.black,
   StyleParams.linkFont: UIFont.systemFont(ofSize: 15, weight: .regular),
   StyleParams.linkFontColor: UIColor.red,
   StyleParams.linkFontHightlightColor: UIColor.red
])
bodyTextView.attributeText = bodyHTML
public enum StyleParams {
    //Headers (<h1>..<h5>)
    case titleH1Font // h1
    case titleH1Color
    case titleH1BackgroundColor
    case titleH2Font // h2
    case titleH2Color
    case titleH2BackgroundColor
    case titleH3Font // h3
    case titleH3Color
    case titleH3BackgroundColor
    case titleH4Font // h4
    case titleH4Color
    case titleH4BackgroundColor
    case titleH5Font // h5
    case titleH5Color
    case titleH5BackgroundColor
    //Links (<a>)
    case linkFont
    case linkFontColor
    case linkFontHightlightColor
    case linkBackgroundColor
    //Paragraph (<p>)
    case paragraphFont
    case paragraphColor
    case paragraphBackgroundColor
    //Img
    case imgPlaceholder
}

extension String {

    public func toHTML(params: [StyleParams:Any] = [:]) -> AttributedText {

        let listHeaders = ["h1", "h2", "h3", "h4", "h5"]

        let style = self.style(tags: [
            headerStyle(headers: listHeaders, params: params),
            paragraphStyle(params: params),
            linkStyle(params: params)
        ])

        return imageStyle(
            style: style,
            params: params
        ).styleHashtags(
                linkHashtag(params: params)
        ).styleMentions(
                linkStyle(params: params)
        ).styleLinks(
                linkStyle(params: params)
        )

        //OnClick works well without parse to NSAttributeString/NSMutableAttributedString
       /* return style
            .styleHashtags(
                linkHashtag(params: params)
            ).styleMentions(
                linkStyle(params: params)
            ).styleLinks(
                linkStyle(params: params)
            )*/
    }

    private func imageStyle() -> Style {
        return Style("img")
    }

    private func imageStyle(style: AttributedText, params: [StyleParams: Any] = [:]) -> NSMutableAttributedString {

        let mutableAttrStr = NSMutableAttributedString(attributedString: style.attributedString)

        var placeHodler = UIImage(named: "forum_empty_avatar")
        if let imageNamed = params[.imgPlaceholder] as? String {
            placeHodler = UIImage(named: imageNamed)
        }

        var locationShift = 0
        for detection in style.detections {
            switch detection.type {
            case .tag(let tag):
                if let imgSrc = tag.attributes["scr"] {

                    let imageView = UIImageView()

                    if let url = URL(string: imgSrc) {
                        imageView.af.setImage(withURL: url, placeholderImage: placeHodler)
                    } else {
                        imageView.image = placeHodler
                    }

                    let textAttachment = NSTextAttachment()
                    textAttachment.image = imageView.image

                    let imageAttrStr = NSAttributedString(attachment: textAttachment)
                    let nsrange = NSRange(detection.range, in: mutableAttrStr.string)
                    mutableAttrStr.insert(imageAttrStr, at: nsrange.location + locationShift)
                    locationShift += 1
                }
                break
            default:
                break
            }
        }

        return mutableAttrStr
    }

    private func linkHashtag(params: [StyleParams:Any] = [:]) -> Style {

        var styleHashtag = Style.font(UIFont.systemFont(ofSize: 15))

        if let font = params[.linkFont] as? UIFont {
            styleHashtag = styleHashtag.font(font)
        }

        return styleHashtag
    }

    private func linkStyle(params: [StyleParams:Any] = [:]) -> Style {

        var styleLink = Style("a")

        if let font = params[.linkFont] as? UIFont {
            styleLink = styleLink.font(font)
        }

        if let color = params[.linkFontColor] as? UIColor {
            styleLink = styleLink.foregroundColor(color, .normal)
        } else {
            fatalError("You need add font color to <a> tag.")
        }

        if let color = params[.linkFontHightlightColor] as? UIColor {
            styleLink = styleLink.foregroundColor(color, .highlighted)
        } else {
            fatalError("You need add font hightlight color to <a> tag.")
        }

        if let backgroundColor = params[.linkBackgroundColor] as? UIColor {
            styleLink = styleLink.backgroundColor(backgroundColor)
        }

        return styleLink
    }

    private func paragraphStyle(params: [StyleParams:Any]) -> Style {

        var styleLink = Style("p")

        if let font = params[.paragraphFont] as? UIFont {
            styleLink = styleLink.font(font)
        }

        if let color = params[.paragraphColor] as? UIColor {
            styleLink = styleLink.foregroundColor(color)
        }

        if let backgroundColor = params[.paragraphBackgroundColor] as? UIColor {
            styleLink = styleLink.backgroundColor(backgroundColor)
        }

        return styleLink
    }

    private func headerStyle(headers: [String], params: [StyleParams:Any]) -> Style {

        var styleLink = Style("h1")
        for header in headers {
            styleLink = Style(header)
            switch header {
                case "h1": styleLink = headerH1Style(style: styleLink, params: params)
                    break
                case "h2": styleLink = headerH2Style(style: styleLink, params: params)
                    break
                case "h3": styleLink = headerH3Style(style: styleLink, params: params)
                    break
                case "h4": styleLink = headerH4Style(style: styleLink, params: params)
                    break
                case "h5": styleLink = headerH5Style(style: styleLink, params: params)
                    break
                default:  styleLink = headerH5Style(style: styleLink, params: params)
                    break
            }
        }

        return styleLink
    }

    private func headerH1Style(style: Style, params: [StyleParams:Any]) -> Style {

        var style = style
        if let font = params[.titleH1Font] as? UIFont {
            style = style.font(font)
        }

        if let color = params[.titleH1Color] as? UIColor {
            style = style.foregroundColor(color)
        }

        if let color = params[.titleH1BackgroundColor] as? UIColor {
            style = style.backgroundColor(color)
        }

        return style
    }
}
psharanda commented 4 years ago

Looks like connected with this one https://github.com/psharanda/Atributika/issues/130

tarun-personatech commented 1 year ago

@jordizspmobile I just need to show the image and do not need the tap. In that case, your code should download the image and show it but I was wondering does it ever show the image. Because when the attributed string is built the image is not present, it is being downloaded, and after that textAttachment's image is set. By this time the attributed string is already shown. Setting the image on textAttachment after that has no effect in my case. How do we update the textAttachment after the image is downloaded?