Code Monkey home page Code Monkey logo

activelabel.swift's Introduction

ActiveLabel.swift Carthage compatible Build Status

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

Features

  • Swift 5.0 (1.1.0+) and 4.2 (1.0.1)
  • Default support for Hashtags, Mentions, Links, Emails
  • Support for custom types via regex
  • Ability to enable highlighting only for the desired types
  • Ability to trim urls
  • Super easy to use and lightweight
  • Works as UILabel drop-in replacement
  • Well tested and documented

Install (iOS 10+)

Carthage

Add the following to your Cartfile and follow these instructions

github "optonaut/ActiveLabel.swift"

CocoaPods

CocoaPods 0.36 adds supports for Swift and embedded frameworks. To integrate ActiveLabel into your project add the following to your Podfile:

platform :ios, '10.0'
use_frameworks!

pod 'ActiveLabel'

Usage

import ActiveLabel

let label = ActiveLabel()
label.numberOfLines = 0
label.enabledTypes = [.mention, .hashtag, .url, .email]
label.text = "This is a post with #hashtags and a @userhandle."
label.textColor = .black
label.handleHashtagTap { hashtag in
    print("Success. You just tapped the \(hashtag) hashtag")
}

Custom types

let customType = ActiveType.custom(pattern: "\\swith\\b") //Regex that looks for "with"
label.enabledTypes = [.mention, .hashtag, .url, .email, customType]
label.text = "This is a post with #hashtags and a @userhandle."
label.customColor[customType] = UIColor.purple
label.customSelectedColor[customType] = UIColor.green
label.handleCustomTap(for: customType) { element in
    print("Custom type tapped: \(element)")
}

Enable/disable highlighting

By default, an ActiveLabel instance has the following configuration

label.enabledTypes = [.mention, .hashtag, .url, .email]

But feel free to enable/disable to fit your requirements

Batched customization

When using ActiveLabel, it is recommended to use the customize(block:) method to customize it. The reason is that ActiveLabel is reacting to each property that you set. So if you set 3 properties, the textContainer is refreshed 3 times.

When using customize(block:), you can group all the customizations on the label, that way ActiveLabel is only going to refresh the textContainer once.

Example:

label.customize { label in
    label.text = "This is a post with #multiple #hashtags and a @userhandle."
    label.textColor = UIColor(red: 102.0/255, green: 117.0/255, blue: 127.0/255, alpha: 1)
    label.hashtagColor = UIColor(red: 85.0/255, green: 172.0/255, blue: 238.0/255, alpha: 1)
    label.mentionColor = UIColor(red: 238.0/255, green: 85.0/255, blue: 96.0/255, alpha: 1)
    label.URLColor = UIColor(red: 85.0/255, green: 238.0/255, blue: 151.0/255, alpha: 1)
    label.handleMentionTap { self.alert("Mention", message: $0) }
    label.handleHashtagTap { self.alert("Hashtag", message: $0) }
    label.handleURLTap { self.alert("URL", message: $0.absoluteString) }
}

Trim long urls

You have the possiblity to set the maximum lenght a url can have;

label.urlMaximumLength = 30

From now on, a url that's bigger than that, will be trimmed.

https://afancyurl.com/whatever -> https://afancyurl.com/wh...

API

mentionColor: UIColor = .blueColor()
mentionSelectedColor: UIColor?
hashtagColor: UIColor = .blueColor()
hashtagSelectedColor: UIColor?
URLColor: UIColor = .blueColor()
URLSelectedColor: UIColor?
customColor: [ActiveType : UIColor]
customSelectedColor: [ActiveType : UIColor]
lineSpacing: Float?
handleMentionTap: (String) -> ()
label.handleMentionTap { userHandle in print("\(userHandle) tapped") }
handleHashtagTap: (String) -> ()
label.handleHashtagTap { hashtag in print("\(hashtag) tapped") }
handleURLTap: (NSURL) -> ()
label.handleURLTap { url in UIApplication.shared.openURL(url) }
handleEmailTap: (String) -> ()
label.handleEmailTap { email in print("\(email) tapped") }
handleCustomTap(for type: ActiveType, handler: (String) -> ())
label.handleCustomTap(for: customType) { element in print("\(element) tapped") }
filterHashtag: (String) -> Bool
label.filterHashtag { hashtag in validHashtags.contains(hashtag) }
filterMention: (String) -> Bool
label.filterMention { mention in validMentions.contains(mention) }

Alternatives

Before writing ActiveLabel we've tried a lot of the following alternatives but weren't quite satisfied with the quality level or ease of usage, so we decided to contribute our own solution.

  • TTTAttributedLabel (ObjC) - A drop-in replacement for UILabel that supports attributes, data detectors, links, and more
  • STTweetLabel (ObjC) - A UILabel with #hashtag @handle and links tappable
  • AMAttributedHighlightLabel (ObjC) - A UILabel subclass with mention/hashtag/link highlighting
  • KILabel (ObjC) - A simple to use drop in replacement for UILabel for iOS 7 and above that highlights links such as URLs, twitter style usernames and hashtags and makes them touchable

activelabel.swift's People

Contributors

amoiz-cc avatar bampt avatar casperson avatar clineu avatar cruisediary avatar demonar avatar econa77 avatar efremidze avatar ejoebstl avatar filipealva avatar isapozhnik avatar jeehut avatar kaandedeoglu avatar krjackso avatar lancheg avatar lasha-ring avatar lastmove avatar marvinnazari avatar maziyarpanahi avatar mehulmodihb avatar paullafytskyi avatar polqf avatar schickling avatar sherwinzadeh avatar tony-pizza avatar wowbroforce avatar xuaninbox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

activelabel.swift's Issues

Custom hyperlink

1、How custom hyperlink? like this “annawildgirl”
Image of Yaktocat

2、Click or long Angao Liang effect
Image of Yaktocat
Has been fully migrated to the swift, younger brother Fucai, try modified, but no modification is successful, I hope you can solve these two problems as soon as possible, thank you very much

Does not support Chinese

Thank you develop the library, but does not support Chinese, looked at them, use regular expressions, no modification is successful, I hope you can support the Chinese as soon as possible, thank you.

Unable to get .handleURLTap() to work

1st off, this is awesome. Thank you for the work and effort!

I have ActiveLabel in a UITableViewCell:

let label = ActiveLabel()
var urlToLoad: String = ""

lazy var bodyLabel: UILabel = {
    let label = ActiveLabel()
    label.URLColor = UIColor(red: 85.0/255, green: 238.0/255, blue: 151.0/255, alpha: 1)
    label.URLSelectedColor = UIColor(red: 82.0/255, green: 190.0/255, blue: 41.0/255, alpha: 1)
    label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
    label.numberOfLines = 0
    label.lineSpacing = 4
    return label
}()

It works fine and picks up the URL's
If I put the .handleURLTap { self.alert("URL", message: $0.absoluteString) } code in the UITableViewCell then the code works and is executed but I can't segue from with UITableViewCell.

If I put the code into:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = ...  // code removed 
cell.label.handleURLTap({ NSURL in print("Success. Loading  \(NSURL)")})
..
}

How do I call the URL thats inside the UITableViewCell from the UIViewController so I can load that URL?

empty space interaction on the last line

simulator screen shot 21 mar 2016 5 55 08 pm

I supposed it should not invoke the tapHandler when tapping space with no text on the last line, as shown in attached screenshot. Does it makes sense to you?

Support Truncation Token

able to set numberOfLines = 4 once the text over 4 lines it will show ...More? And, ...More able to let user to press it.

Working with UIScrollView

I'm having issues getting this working with UIScrollViews. UIScrollView doesn't seem to allow for interaction with the linking.

Nothing

Sorry, please ignore this issue

Adding Accessibility support

I'm interested in making the Mention, Hashtag, and URL elements accessible. We need this for our automated test suite, which relies on accessibility labels. But it's also just a good thing to provide for people who need it.

I'm not 100% sure how to do it, but I'll try to copy what TTTAttributedLabel is doing.

String height calculations are not respected by ActiveLabel

Hey, I'm setting up my label in a UICollectionViewCell subclass where I calculate the label height beforehand, but I'm seeing clipping when I switch from UILabel to ActiveLabel. Here's my code snippet. (I also see the same results when I use sizeThatFits to calculate the height)

let attributedComment = NSMutableAttributedString(string: comment, attributes: [NSFontAttributeName : PostContentView.commentFont, NSParagraphStyleAttributeName : Utility.paragraphStyle])
let height = attributedComment.heightWithConstrainedWidth(frame.size.width)
let commentLabel = ActiveLabel(frame: CGRect(x: 0, y: 0, width: CGRectGetWidth(frame), height: height))
commentLabel.numberOfLines = 0
commentLabel.attributedText = attributedComment
addSubview(commentLabel)


//NSAttributedString Extension
extension NSAttributedString {
    func heightWithConstrainedWidth(width: CGFloat) -> CGFloat {
        let constraintRect = CGSize(width: width, height: CGFloat.max)
        let boundingBox = self.boundingRectWithSize(constraintRect, options: NSStringDrawingOptions.UsesLineFragmentOrigin, context: nil)

        return ceil(boundingBox.height)
    }
}

Below are two images using ActiveLabel and UILabel, respectively.

screen shot 2015-10-07 at 3 57 52 pm

screen shot 2015-10-07 at 4 01 43 pm

Is there a gotcha that I'm missing here? (the paragraph style just provides a lineSpacing value of 6 btw)

numberOfLines and truncation does not seem to be supported

Hello,

I just wanted to mention that numberOfLines and truncation does not seem to be supported at all. I took a look at the code, and I'm not even sure how it should be done when using NSLayoutManager and NSTextContainer, etc.

Was wondering if a maintainer might be able to help include support for this? I think it's a pretty important feature to be missing, and this is not really a drop-in replacement for UILabel until it works.

Links without http and https are not clickable

Update the below method
private func didTapStringURL(var stringURL: String) {
if !(stringURL.containsIgnoreCase("http")) {
stringURL = "http://" + stringURL
}
guard let urlHandler = urlTapHandler, let url = NSURL(string: stringURL) else {
delegate?.didSelectText(stringURL, type: .URL)
return
}
urlHandler(url)
}

AutoLayout and custom font

This was mentioned in a another issue but my problem is specific to font.

When I use a custom font, PTSans, the text is cut off at the end.

this is what it should look like:

screen shot 2016-02-11 at 12 43 02 pm

And this is what it looks with with ActiveLabel

screen shot 2016-02-11 at 12 43 58 pm

Notice the text and time stamp at the end is missing

This is a label inside a tableview cell using Automatic dimensions for the height. the cell height changes based on the size of the label.

Remove Parts of detected String

Hi,
I successfully modified the detection String for my needs to match @userName:userId;

Where would I best modify the lib to cutoff :userId; during rendering, but keep the original object intact? Optionally also just cutting down to username would be fine ^^

Handle tap is triggered on scroll when touch down started on a url/mention/hashtag

When scrolling on a table view and a tap is done on a url (for example), the handleTap block is triggered (opening a web browser in my case) thus troubling the experience of the users when they only want to scroll. Could it be possible to trigger the block exclusively on touch up? (or if not, add an option to it?)
Great plug-in by the way!

It seems that handle taps do not work 0.3.7

Thank you guys! It's the best what I've seen!
So I'm not sure but it seems like handles do not work in 0.3.7. I downloaded and compiled source code folder 0.3.7 and 0.3.6. In 0.3.6 handleMentionTap, handleHashtagTap, handleURLTap working as expected. In 0.3.7 they don't work.

Partially working on Custom UITableViewCell

Handle functions are not working until scrolling the tableview. While scrolling handle functions are working for a moment. It's hard to describe but, cell is on Storyboard. It has single UILabel and constraints, using system-regular font and it's multiline.

Do you know anything about this issue?

By the way, it is working on clean UITableView. Unfortunately it is not possible to start making the tableview from the beginning.

Than you in advance.

Expose rect of tapped element

I'd like to know the tap location (or rect of tapped hashtag) so that I can present a popover (on iPad) at the correct location.

screen shot 2016-06-01 at 17 24 42

activeLabel.text = "This is a @test with various @mentions and #hashtags and http://urls.com"
activeLabel.handleHashtagTap { (hashtag) in       
    let alert = UIAlertController(title: "open #" + hashtag, message: "", preferredStyle: .ActionSheet)
    alert.addAction(UIAlertAction(title: "Twitter", style: .Default, handler: nil))
    alert.addAction(UIAlertAction(title: "Instagram", style: .Default, handler: nil))

    let popover = alert.popoverPresentationController
    if let popover = popover {
        popover.sourceView = self.activeLabel
        popover.sourceRect = self.activeLabel.bounds
        popover.permittedArrowDirections = .Any
    }
    self.presentViewController(alert, animated: true, completion: nil)
}

You can see in the above that I'm using the ActiveLabel as the source view, so the popover's arrow comes from the edge of it, but it would be neat to know where the actual word is, so the arrow can point to it.

Handle tap if not on ActiveElement

Is there a way to handle all other taps on the label that don't match a specific active element, without breaking the active element detection?

URL Detection

I noticed in my app that the word "picture" is detected as a URL. After looking into it, I noticed that any phrase starting with "www" or "pic" will get detected as a URL, even though they are not valid URLs. Looking at the history, we used to use NSDataDetector but switched to a regex. Is there a compelling reason not to use NSDataDetector for this? The only test that fails using NSDataDetector is that "google.com" gets detected as a URL when the test does not want it to be (I would actually prefer it to be). Right now I am running with my fork that uses NSDataDetector until this can be resolved.

Events and scroll conflict

Events and scroll conflict
I came to ask questions of, UILabel added to the UITableViewCell, slide UILabel not scroll.
Know where the problems userInteractionEnabled = true, how do you solve it?

Bad test design

Just a pedant issue!

While playing and trying to improve the regexs i found a bad test design. Your test code is written to succeed, but does not handle failure well. Problem is caused because you don't prevent "inner" assertion execution when "outer" asserts fails and this leads to a currentElementString/currentElementType .first! un-wrapp crash.

E.g, this test of comma-mention test cause an exception instead of an assertion failure.

label.text = ",@userhandle"
XCTAssertEqual(activeElements.count, 1) // This evaluates to false, which is OK
XCTAssertEqual(currentElementString, "userhandle") // But then currentElementString will fail becouse activeElements.first!
XCTAssertEqual(currentElementType, ActiveType.Mention)

This would be the good way; test fails without crashing:

label.text = ",@userhandle"
XCTAssertEqual(activeElements.count, 1)
if(activeElements.count>0) {
    XCTAssertEqual(currentElementString, "userhandle")
    XCTAssertEqual(currentElementType, ActiveType.Mention)
}      

Add detection for .@username

The dot+user mention(.@poolqf) is a common pattern in twitter for example.
Maybe we could add this exception in the regex.

@schickling could you help me on that? I'm not very good with regex

Thanks

Error in Xcode 6.4

I use cocoapods to install this library but it shows errors everywhere.. can you tell me how to fix those errors? or is it can't be used in Xcode 6.4 ? thank you..
screen shot 2015-12-02 at 12 52 15 pm

CocoaPods Support

Can we add cocoapods support? I can make a pull request with cocoapods support.

Optimization: Do simple checks before using regex.

I've seen this idea in a Twitter java implementation.

  • Urls: Check if text contains '.' or ':'. If text doesn't contain '.' or ':' at all, text doesn't contain URL so skip url regex.
  • Mentions: Check if text contains '@' . If text doesn't contain '@' at all, text doesn't contain mentions so skip mention regex.
  • Hashtags: Check if text contains '#' . If text doesn't contain '#' at all, text doesn't contain hashtags so skip hashtag regex.

Note this checking might be optional. If developer knows for sure the texts will contain one of the elements, then this is actually slowing execution (but i think its a very very rare scenario). Also, i don't know the real impact of this in Swift since i havn't tested yet, but I guess this should improve since character search should be way faster than regex eval.

Source of the idea & good resource for regexs and matching:
https://github.com/twitter/twitter-text/blob/810c05271c6d295211760139f4ab04749495b38a/java/src/com/twitter/Extractor.java

No Objective-C?

We really need Objective-C here as others have abandoned their projects

Porting to objective-c

I'm thinking to port the ActiveLabel into objective-c in case if anyone needs it without bridging objc/swift. Do you guys its worth to do this? I'd like to hear any advices!

Duplicate text label when I'm using TableView or CollectionView

The API doesn't work so well when I'm using TableView or CollectionView... sometimes the label doesn't have text and the cell is getting the old value for this label... duplicating my text and showing wrong.

To fix that... I've had to improve some part of the code... see my solution:

override public var text: String? {
didSet {
if text != nil && text != "" {
updateTextStorage()
} else {
self.textStorage.setAttributedString(NSAttributedString())
}
}
}

I hope help someone else and/or improve your code... Great API that you make... Congrats

AutoLayout Not Working Correctly with ActiveLabel

I'm using ActiveLabel as the Custom Class for my post’s content labels in the Storyboard; however, I believe it is interfering with Autolayout. For some reason the post’s content gets cut off (screenshot attached).

I guess it’s hard to know without seeing my code - but do you have a clue why this might be?

img_1008

Linkable works only in first paragraph.

How can l make the all hashtags, mentions, and links linkable in this label below.
2015-12-23 18 50 12
Including line breaks in texts, linkable doesn't work too.
What I miss something when the label's set upped ?
Thanks in beforehand.

Usernames with periods are not fully highlighted

Would it be possible to expand the search for usernames to include ones that have periods in them, such as my name "@alex.figueroa"? It is currently being highlighted only until the period. (It seems Github does this with their markdown too 😅)

activelabelscreenshot

NSRangeException

Hi Johannes, thanks for ActiveLabel - nice work!

I notice that when using ActiveLabel in a CollectionView and scrolling up or down, I sometimes get the following error:

*** Terminating app due to uncaught exception 'NSRangeException', reason: 'NSMutableRLEArray objectAtIndex:effectiveRange:: Out of bounds'

This has also been documented in another UILabel github lib (Obj-C), here:

SebastianThiebaud/STTweetLabel#55

The bug is hard to reproduce, but it has happened multiple times. It doesn't appear to be associated with the speed of scrolling or anything else I can reproduce, but as I mentioned, it has happened multiple times now, and it does seem related to ActiveLabel. Sorry I can't be of more help.

Support Phone Number detect

Hi,
ActiveLabel.swift is very useful and thank you.

I have a suggestion. Phone Number is important element is phone system. it looks like URL.
How about support detect Phone Number?

Vertical align not correct after setting textAligment

Hi,

I am using ActiveLabel in my app, and there's a case where I need the text to be horizontal and vertical aligned. But setting the textAlignment property does not change anything.

I added a line on the addLineBreak(attrString:) method expecting it to center the text as expected, but the result is the following:

screen shot 2015-12-05 at 19 32 29

Correct center alignment, but incorrect vertical one.

/// add line break mode
    private func addLineBreak(attrString: NSAttributedString) -> NSMutableAttributedString {
        let mutAttrString = NSMutableAttributedString(attributedString: attrString)

        var range = NSRange(location: 0, length: 0)
        var attributes = mutAttrString.attributesAtIndex(0, effectiveRange: &range)

        let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSMutableParagraphStyle ?? NSMutableParagraphStyle()
        paragraphStyle.lineBreakMode = NSLineBreakMode.ByWordWrapping
        paragraphStyle.alignment = textAlignment //ADDED THIS
        if let lineSpacing = lineSpacing {
            paragraphStyle.lineSpacing = CGFloat(lineSpacing)
        }

        attributes[NSParagraphStyleAttributeName] = paragraphStyle
        mutAttrString.setAttributes(attributes, range: range)

        return mutAttrString
    }

A solution to accomplish the center alignment would be to add a didSet on it, as on the other variables.
As for the vertical alignment, I tried a lot of things to accomplish it, but without luck, so I have no clues...

PS. I have an extension of ActiveLabel that uses a delegate instead of having to set block for each Label you create to handle user touches. Do you want me to create a PR with it?

Autoshrink does not work when using ActiveLabel

I am trying to make the label autoshrink by setting in the interface builder: Autoshrink: Minimum Font Size 9, but it has no effect. I want to make the label gets smaller to make the whole string fit inside the UILabel´s frame. Any fixes for this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.