Convert text with HTML tags, links, hashtags, mentions into NSAttributedString. Make them clickable with UILabel drop-in replacement.

Overview


Language: Swift CocoaPods Carthage

Atributika is an easy and painless way to build NSAttributedString. It is able to detect HTML-like tags, links, phone numbers, hashtags, any regex or even standard ios data detectors and style them with various attributes like font, color, etc. Atributika comes with drop-in label replacement AttributedLabel which is able to make any detection clickable

Intro

NSAttributedString is really powerful but still a low level API which requires a lot of work to setup things. It is especially painful if string is template and real content is known only in runtime. If you are dealing with localizations, it is also not easy to build NSAttributedString.

Oh wait, but you can use Atributika!

let b = Style("b").font(.boldSystemFont(ofSize: 20)).foregroundColor(.red)
        
label.attributedText = "Hello World!!!".style(tags: b).attributedString

Yeah, that's much better. Atributika is easy, declarative, flexible and covers all the raw edges for you.

Features

  • NEW! AttributedLabel is a drop-in label replacement which makes detections clickable and style them dynamically for normal/highlighted/disabled states.
  • detect and style HTML-like tags using custom speedy parser
  • detect and style hashtags and mentions (i.e. #something and @someone)
  • detect and style links and phone numbers
  • detect and style regex and NSDataDetector patterns
  • style whole string or just particular ranges
  • ... and you can chain all above to parse some uber strings!
  • clean and expressive api to build styles
  • separate set of detection utils, in case you want to use just them
  • + operator to concatenate NSAttributedString with other attributed or regular strings
  • works on iOS, tvOS, watchOS, macOS

Examples

Detect and style tags, provide base style for the rest of string, don't forget about special html symbols

let redColor = UIColor(red:(0xD0 / 255.0), green: (0x02 / 255.0), blue:(0x1B / 255.0), alpha:1.0)
let a = Style("a").foregroundColor(redColor)

let font = UIFont(name: "AvenirNext-Regular", size: 24)!
let grayColor = UIColor(white: 0x66 / 255.0, alpha: 1)
let all = Style.font(font).foregroundColor(grayColor)

let str = "<a>tributik</a>"
    .style(tags: a)
    .styleAll(all)
    .attributedString

Detect and style hashtags and mentions

let str = "#Hello @World!!!"
    .styleHashtags(Style.font(.boldSystemFont(ofSize: 45)))
    .styleMentions(Style.foregroundColor(.red))
    .attributedString

Detect and style links

let str = "Check this website http://google.com"
    .styleLinks(Style.foregroundColor(.blue))
    .attributedString

Detect and style phone numbers

let str = "Call me (888)555-5512"
    .stylePhoneNumbers(Style.foregroundColor(.red))
    .attributedString

Uber String

    let links = Style.foregroundColor(.blue)
    let phoneNumbers = Style.backgroundColor(.yellow)
    let mentions = Style.font(.italicSystemFont(ofSize: 12)).foregroundColor(.black)
    let b = Style("b").font(.boldSystemFont(ofSize: 12))
    let u = Style("u").underlineStyle(.styleSingle)
    let all = Style.font(.systemFont(ofSize: 12)).foregroundColor(.gray)
    
    let str = "@all I found really nice framework to manage attributed strings. It is called Atributika. Call me if you want to know more (123)456-7890 #swift #nsattributedstring https://github.com/psharanda/Atributika"
        .style(tags: u, b)
        .styleMentions(mentions)
        .styleHashtags(links)
        .styleLinks(links)
        .stylePhoneNumbers(phoneNumbers)
        .styleAll(all)
        .attributedString

AttributedText

Atributika APIs styleXXX produce AttributedText which can be converted into NSAttributedString. Basically AttributedText just contains string, base style and all the detections.

AttributedLabel

AttributedLabel is able to display AttributedText and makes detections clickable if style contains any attributes for .highlighted

<-- detections with this style will be clickable now tweetLabel.attributedText = "@e2F If only Bradley's arm was longer. Best photo ever.😊 #oscars https://pic.twitter.com/C9U5NOtGap
Check this link" .style(tags: link) .styleHashtags(link) .styleMentions(link) .styleLinks(link) .styleAll(all) tweetLabel.onClick = { label, detection in switch detection.type { case .hashtag(let tag): if let url = URL(string: "https://twitter.com/hashtag/\(tag)") { UIApplication.shared.openURL(url) } case .mention(let name): if let url = URL(string: "https://twitter.com/\(name)") { UIApplication.shared.openURL(url) } case .link(let url): UIApplication.shared.openURL(url) case .tag(let tag): if tag.name == "a", let href = tag.attributes["href"], let url = URL(string: href) { UIApplication.shared.openURL(url) } default: break } } view.addSubview(tweetLabel) ">
let tweetLabel = AttributedLabel()

tweetLabel.numberOfLines = 0

let all = Style.font(.systemFont(ofSize: 20))
let link = Style("a")
    .foregroundColor(.blue, .normal)
    .foregroundColor(.brown, .highlighted) // <-- detections with this style will be clickable now

tweetLabel.attributedText = "@e2F If only Bradley's arm was longer. Best photo ever.😊 #oscars https://pic.twitter.com/C9U5NOtGap
Check this \"
https://github.com/psharanda/Atributika\">link" .style(tags: link) .styleHashtags(link) .styleMentions(link) .styleLinks(link) .styleAll(all) tweetLabel.onClick = { label, detection in switch detection.type { case .hashtag(let tag): if let url = URL(string: "https://twitter.com/hashtag/\(tag)") { UIApplication.shared.openURL(url) } case .mention(let name): if let url = URL(string: "https://twitter.com/\(name)") { UIApplication.shared.openURL(url) } case .link(let url): UIApplication.shared.openURL(url) case .tag(let tag): if tag.name == "a", let href = tag.attributes["href"], let url = URL(string: href) { UIApplication.shared.openURL(url) } default: break } } view.addSubview(tweetLabel)

Requirements

Current version is compatible with:

  • Swift 4.0+ (for Swift 3.2 use swift-3.2 branch)
  • iOS 8.0 or later
  • tvOS 9.0 or later
  • watchOS 2.0 or later
  • macOS 10.10 or later

Note: AttributedLabel works only on iOS

Why does Atributika have one 't' in its name?

Because in Belarusian/Russian we have one letter 't' (атрыбутыка/атрибутика). So basically it is transcription, not real word.

Integration

Carthage

Add github "psharanda/Atributika" to your Cartfile

CocoaPods

Atributika is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "Atributika"

Manual

  1. Add Atributika to you project as a submodule using git submodule add https://github.com/psharanda/Atributika.git
  2. Open the Atributika folder & drag Atributika.xcodeproj into your project tree
  3. Add Atributika.framework to your target's Link Binary with Libraries Build Phase
  4. Import Atributika with import Atributika and you're ready to go

License

Atributika is available under the MIT license. See the LICENSE file for more info.

Comments
  • 4.4.0 And missing Symbols

    4.4.0 And missing Symbols

    Just pod install'd 4.4.0 and am getting the following:

    Undefined symbols for architecture arm64:
      "protocol witness table for __C.NSAttributedStringKey : Swift.Hashable in Foundation", referenced from:
          function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Dead> of generic specialization <preserving fragile attribute, Atributika.StyleType, [__C.NSAttributedStringKey : Any]> of static (extension in Swift):Swift._NativeDictionaryBuffer< where A: Swift.Hashable>.fromArray([(A, B)]) -> Swift._NativeDictionaryBuffer<A, B> in RegulationDetailController.o
    ld: symbol(s) not found for architecture arm64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    I'm not sure this is actually Atributika's problem yet but it's in the error so I thought I"d put it here while I try to track down what is going on. Anyone else see this?

    opened by rblalock 17
  • Detection Button frame issue when using custom font

    Detection Button frame issue when using custom font

    I have integrated this in tableview cells with automatic layout. Text is displayed properly, however button frame for touch detection appears to be incorrect. PFA the log and the xcode view debugger image. You can check 3 butons frame's. Frame for the 5 points is completely off the track.

    button issue

    points1.txt

    bug 
    opened by Ankish 11
  • A-Tags

    A-Tags

    Hey! I'm trying to figure out how to convert <a> Tags into clickable links. I tried .style(links:) but that wouldn't work. Do I miss something very obvious?

    opened by 343max 10
  • Style.swift Crash

    Style.swift Crash

    Hi , Style.Swift line 84 and AttributedText.swift line 99 crash , swift 4.1 , Xcode 9 latest version , ios 11 , just copy pasted your "link" and "all" code and crashesh without doing anything else here's the code : `let tweetLabel = AttributedLabel()

        let all = Style.font(.systemFont(ofSize: 20))
        let link = Style
            .foregroundColor(.blue, .normal)
    
    
        tweetLabel.attributedText = "@Hey"
            .styleHashtags(link)
            .styleMentions(link)
            .styleLinks(link)
            .styleAll(all)`
    

    adding .foregroundColor(.brown, .highlighted) to link will result. style.swift to crash o.w AttributeText.swift does

    opened by Mahan3340 10
  • styleMentions returning wrong array detection when used with Korean tags

    styleMentions returning wrong array detection when used with Korean tags

    I have used Atributika Pod in achieving one of the task in my project. I have attributed textview in which mentions, links, hashtags are used. While resetting the attributes of the textview and sending it over the web service I need to extract all the attributed text of the textview which I do using styleMentions(which returns detections array) of Atributika. When mentions are in english it returns one detection for one mention but when Korean mentions are there it returns multiple detections sometimes. Please find textfile attached of the array detections. If you search for the key "mention" you will get 5 results whereas I should have only two one for each mention.

    I have used these two lines of code to extract the detections: let attArray = self.textviewEditMode?.attributedText.styleMentions(.backgroundColor(Fp_s29.color)) let detections = attArray?.detections whereas, textviewEditMode- is my UITextview Fp_s29.color- is my predefined color

    Please let me know if something needs to be done. Thank you!

    mentionsDetection.zip

    opened by manisingh41 9
  • Handling unavailable fonts/font trait combinations (styles)

    Handling unavailable fonts/font trait combinations (styles)

    Hi there!

    I've been trying out Atributika to see if it could be a fit for my project to replace the current (and slow) implementation with plain NSAttributedString (from HTML).

    I've got it working ~90%. An issue I have been facing since implementing this feature, even with the NSAttributedString implementation, is that I (currently) do not own the font we use in all different combinations of bold, italic, underlined, etc. Though the one we are currently mostly facing is bold + italic.

    In our current implementation we used the following code snippet to basically fall back to a similarly looking free font in such cases:

    extension NSMutableAttributedString {
        func setBaseFont(baseFont: UIFont, preserveFontSizes: Bool = false) {
            let baseDescriptor = baseFont.fontDescriptor
            let wholeRange = NSRange(location: 0, length: length)
            let fallbackFont = UIFont(name: "HelveticaNeue-Light", size: 15)!
    
            beginEditing()
            enumerateAttribute(.font, in: wholeRange, options: []) { object, range, _ in
                guard let font = object as? UIFont else { return }
                var descriptor: UIFontDescriptor
                var newSize: CGFloat
                let traits = font.fontDescriptor.symbolicTraits
    
                if traits.contains(.traitItalic) {
                    descriptor = fallbackFont.fontDescriptor.withSymbolicTraits(traits)!
                    newSize = descriptor.pointSize
                } else if traits.contains(.traitBold) {
                    descriptor = baseDescriptor.withSymbolicTraits(traits)!
                    newSize = preserveFontSizes ? descriptor.pointSize : baseDescriptor.pointSize
                } else {
                    descriptor = baseDescriptor
                    newSize = preserveFontSizes ? descriptor.pointSize : baseDescriptor.pointSize
                }
    
                let newFont = UIFont(descriptor: descriptor, size: newSize)
                self.removeAttribute(.font, range: range)
                self.addAttribute(.font, value: newFont, range: range)
            }
            endEditing()
        }
    }
    

    Is there something similar to achieve this in Atributika? I know that the easiest way would be to buy the missing combos, but I currently do not have the budget for that.

    Could this be done using the tuner: variant of .style(...)?

    I hope you could help me out with this.

    Happy holidays!

    enhancement 
    opened by philhinco 7
  • Index out of bounds on Add Detection button for strings with emoji and link

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

    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
    
    opened by denniskeithgaso 7
  • Tags at start of text apply throughout text.

    Tags at start of text apply throughout text.

    Starting text with a tag applies that tag throughout, even though there is a matching end tag.

    "Save $1.00 on any order!" comes out all bold.

    "Save $1.00 on any order!" correctly bolds only the two sections.

    With this call:

                let desc = Atributika(text: text,
                                      styles: [
                                        "b" : [.font( UIFont.boldSystemFont(ofSize: 14))]
                    ],
                                      baseStyle: [
                                        .font( UIFont.systemFont(ofSize: 14)), .foregroundColor(UIColor.red)
                    ])
                    .buildAttributedString()
    
    opened by graham-perks-snap 6
  • Emoji Support

    Emoji Support

    I'm currently working with Atributika and my use-case specifically applies to the hashtag functionality. I have noticed that the parser doesn't seem to include emojis in the tag and I was wondering if any of you have a fix/workaround for this?

    To just give you all an example, let's say the hashtag I'm working with is #Atributika❤️, but the parser will only parse up to #Atributika and ignores the emoji.

    let font = Style.font(UIFont.init(name: "OpenSans", size: 12)!).foregroundColor(UIColor.lightGray)
    
    let contentStyling = Style("a")
        .foregroundColor(UIColor.lightGray, .normal)
        .foregroundColor(UIColor.white, .highlighted)
    
    label.lineBreakMode = .byWordWrapping
    label.numberOfLines = 0
    label.attributedText = #Atributika❤️
        .style(tags: contentStyling)
        .styleHashtags(contentStyling)
        .styleMentions(contentStyling)
        .styleLinks(contentStyling)
        .styleAll(font)
    
    label.frame = CGRect(x: 185, y: 140, width: 165, height: 90)
    
    label.onClick = { label, detection in
                switch detection.type {
                case .hashtag(let tag):
                    var passTag = ["tag":"#\(tag)"]
                    self.tapTag = true
                    self.dismiss(animated: true, completion: nil)
                    SVProgressHUD.show(withStatus: "Loading projects")
                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NotificationID"), object: nil, userInfo: passTag as [AnyHashable : Any])
    
                case .mention( _): break
                case .link( _): break
                case .tag( _): break
                default: break
                }
            }
    
    view.addSubview(label)
    
    
    opened by qxtaiba 5
  • Paragraph tag (<p>) doesn't start from new line

    Paragraph tag (

    ) doesn't start from new line

    The below text with paragraph tags doesn't start from new line on every

    tag.

    <p> The first line of the text.</p><p>This second line should start a new paragraph. but it doesn't</p>

    How can I get it working?

    opened by tarun-personatech 5
  • Missing libSwiftCoreGraphics.tbd in project's

    Missing libSwiftCoreGraphics.tbd in project's

    When I upgrade my Xcode to 14, my app crashed and Get an error message: dyld: Library not loaded: /usr/lib/swift/libswiftCoreGraphics.dylib

    It's only happen on devices with iOS version below 13,like iOS 12/11.

    opened by zoloyy 0
  • Fix infinite loop when using `<` or `&` characters with diacritic

    Fix infinite loop when using `<` or `&` characters with diacritic

    The infinite loop problem is caused by different diacritic handling policies. NSScanner.scanUpToCharacters(from:) is not diacritic sensitive but `NSScanner.scanString(_:) is diacritic sensitive.

    opened by wplong11 0
  • Not support SwiftUI

    Not support SwiftUI

    I used Atributika and it is not supported SwiftUI and text is not showing.

    func makeUIView(context: UIViewRepresentableContext) -> UIView { let view = UIView(frame: .zero) view.backgroundColor = .yellow let label = AttributedLabel() label.numberOfLines = 0 label.backgroundColor = .red label.lineBreakMode = .byWordWrapping label.font = type.font label.attributedText = text .attributedText(for: type, alignment: alignment, textColor: textColor, linkColor: linkColor) label.clipsToBounds = true label.sizeToFit() view.addSubview(label)
    label.translatesAutoresizingMaskIntoConstraints = false label.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true label.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true return view }

    func updateUIView(_ label: UIView, context: UIViewRepresentableContext<AtributikaView>) {
    
    }
    
    opened by sharvanksapient 0
  • tag a has problem when set title tag a like url

    tag a has problem when set title tag a like url

    when string tag a is <a href="https://food.trueid.net/detail/WeX36y1rkg7J">www.food.trueid.net Atributika detect title not url in href

    opened by sk-chanch 1
Releases(4.10.1)
Owner
Pavel Sharanda
Software Engineer, iOS
Pavel Sharanda
Generate styled SwiftUI Text from strings with XML tags.

XMLText is a mini library that can generate SwiftUI Text from a given XML string with tags. It uses AttributedString to compose the final text output.

null 15 Dec 7, 2022
A fast & lightweight XML & HTML parser in Swift with XPath & CSS support

Fuzi (斧子) A fast & lightweight XML/HTML parser in Swift that makes your life easier. [Documentation] Fuzi is based on a Swift port of Mattt Thompson's

Ce Zheng 994 Jan 2, 2023
Ji (戟) is an XML/HTML parser for Swift

Ji 戟 Ji (戟) is a Swift wrapper on libxml2 for parsing XML/HTML. Features Build XML/HTML Tree and Navigate. XPath Query Supported. Comprehensive Unit T

HongHao Zhang 824 Dec 15, 2022
Kanna(鉋) is an XML/HTML parser for Swift.

Kanna(鉋) Kanna(鉋) is an XML/HTML parser for cross-platform(macOS, iOS, tvOS, watchOS and Linux!). It was inspired by Nokogiri(鋸). ℹ️ Documentation Fea

Atsushi Kiwaki 2.3k Dec 31, 2022
SwiftSoup: Pure Swift HTML Parser, with best of DOM, CSS, and jquery (Supports Linux, iOS, Mac, tvOS, watchOS)

SwiftSoup is a pure Swift library, cross-platform (macOS, iOS, tvOS, watchOS and Linux!), for working with real-world HTML. It provides a very conveni

Nabil Chatbi 3.7k Jan 6, 2023
📄 A Swift DSL for writing type-safe HTML/CSS in SwiftUI way

?? swift-web-page (swep) Swep is a Swift DSL for writing type-safe HTML/CSS in SwiftUI way. Table of Contents Motivation Examples Safety Design FAQ In

Abdullah Aljahdali 14 Dec 31, 2022
Mongrel is a Swift and HTML hybrid with a bit of support for CSS and Javascript.

Mongrel is a Swift and HTML hybrid with a bit of support for CSS and Javascript. Using a declaritive style of programming, Mongrel makes writing HTML feel natural and easy. Mongrel also uses a SwiftUI like body structure allowing structs to be completely dedicated as an HTML page or element.

Nicholas Bellucci 12 Sep 22, 2022
A sensible way to deal with XML & HTML for iOS & macOS

Ono (斧) Foundation lacks a convenient, cross-platform way to work with HTML and XML. NSXMLParser is an event-driven, SAX-style API that can be cumbers

Mattt 2.6k Dec 14, 2022
An Objective-C framework for your everyday HTML needs.

HTMLKit An Objective-C framework for your everyday HTML needs. Quick Overview Installation Parsing The DOM CSS3 Selectors Quick Overview HTMLKit is a

Iskandar Abudiab 229 Dec 12, 2022
Fetch a XML feed and parse it into objects

AlamofireXmlToObjects ?? This is now a subspec of EVReflection and the code is maintained there. ?? You can install it as a subspec like this: use_fra

Edwin Vermeer 65 Dec 29, 2020
Convert text with HTML tags, links, hashtags, mentions into NSAttributedString. Make them clickable with UILabel drop-in replacement.

Atributika is an easy and painless way to build NSAttributedString. It is able to detect HTML-like tags, links, phone numbers, hashtags, any regex or

Pavel Sharanda 1.1k Jan 8, 2023
Convert text with HTML tags, links, hashtags, mentions into NSAttributedString. Make them clickable with UILabel drop-in replacement.

Convert text with HTML tags, links, hashtags, mentions into NSAttributedString. Make them clickable with UILabel drop-in replacement.

Pavel Sharanda 1.1k Dec 26, 2022
UILabel drop-in replacement supporting Hashtags

ActiveLabel.swift UILabel drop-in replacement supporting Hashtags (#), Mentions (@), URLs (http://), Emails and custom regex patterns, written in Swif

Optonaut 4.2k Dec 31, 2022
A drop-in replacement for UILabel that supports attributes, data detectors, links, and more

TTTAttributedLabel A drop-in replacement for UILabel that supports attributes, data detectors, links, and more TTTAttributedLabel is a drop-in replace

TTTAttributedLabel 8.8k Dec 30, 2022
An iOS text field that represents tags, hashtags, tokens in general.

WSTagsField An iOS text field that represents tags, hashtags, tokens in general. Usage let tagsField = WSTagsField() tagsField.layoutMargins = UIEdgeI

Whitesmith 1.2k Dec 29, 2022
An iOS text field that represents tags, hashtags, tokens in general.

WSTagsField An iOS text field that represents tags, hashtags, tokens in general. Usage let tagsField = WSTagsField() tagsField.layoutMargins = UIEdgeI

Whitesmith 1.2k Jan 9, 2023
JSONHelper - ✌ Convert anything into anything in one operation; JSON data into class instances, hex strings into UIColor/NSColor, y/n strings to booleans, arrays and dictionaries of these; anything you can make sense of!

JSONHelper Convert anything into anything in one operation; hex strings into UIColor/NSColor, JSON strings into class instances, y/n strings to boolea

Baris Sencan 788 Jul 19, 2022
Swift package to convert a HTML table into an array of dictionaries.

Swift package to convert a HTML table into an array of dictionaries.

null 1 Jun 18, 2022
This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "make it black" and change the background color of the view in the frame.

VoiceOperationSample This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "mak

Takuya Aso 3 Dec 3, 2021
Easiest way to create an attributed UITextView (with support for multiple links and from html)

AttributedTextView Easiest way to create an attributed UITextView (with support for multiple links and html). See the demo app and the playground for

Edwin Vermeer 430 Nov 24, 2022