Skip to content

Commit

Permalink
Added "setLink:range:" to NSAttributedString+Attributes so that you c…
Browse files Browse the repository at this point in the history
…an add links directly to the NSAttributedString.

This method is now preferred instead of calling addCustomLink:inRange: on the OHAttributedLabel.
  • Loading branch information
AliSoftware committed Nov 18, 2012
1 parent 3d46deb commit 78b2ff4
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1536</int>
<string key="IBDocument.SystemVersion">12C60</string>
<string key="IBDocument.InterfaceBuilderVersion">2843</string>
<string key="IBDocument.SystemVersion">12C3006</string>
<string key="IBDocument.InterfaceBuilderVersion">2844</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1929</string>
<string key="NS.object.0">1930</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
Expand Down Expand Up @@ -261,7 +261,7 @@ cm5pYSAoKzEgNDA4LTk5Ni0xMDEwKSBuZXh0IHN1bmRheS4</string>
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Some &lt;b&gt;basic&lt;/b&gt; &lt;font name="Courier" size="14"&gt;HTML&lt;/font&gt; support is provided for &lt;u&gt;convenience&lt;/u&gt; too. &lt;font color="red"&gt;You can add your own parsers easily&lt;/font&gt; by subclassing &lt;font name="Courier" size="14"&gt;OHASMarkupParserBase&lt;/font&gt; if needed.</string>
<string key="IBUIText">Some &lt;b&gt;basic&lt;/b&gt; &lt;font name="Courier" size="14"&gt;HTML&lt;/font&gt; support is provided for &lt;u&gt;convenience&lt;/u&gt; too. &lt;font color="red"&gt;You can add your own &lt;a href="http://en.wikipedia.org/wiki/Parser"&gt;parsers&lt;/a&gt; easily&lt;/font&gt; by subclassing &lt;font name="Courier" size="14"&gt;OHASMarkupParserBase&lt;/font&gt; if needed.</string>
<object class="NSColor" key="IBUITextColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDEAA</bytes>
Expand Down Expand Up @@ -291,7 +291,7 @@ cm5pYSAoKzEgNDA4LTk5Ni0xMDEwKSBuZXh0IHN1bmRheS4</string>
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Some *basic* `Markup` is _also_ provided, so that's even _*easier*_ to build `NSAttributedStrings`. {red|*cool*, right?} I hope you {#80c|enjoy} it!</string>
<string key="IBUIText">Some *basic* `Markup` is _also_ provided, so that's even _*easier*_ to build `NSAttributedStrings` with [custom links](http://www.foodreporter.net) and all that. {red|*cool*, right?} I hope you {#80c|enjoy} it!</string>
<reference key="IBUITextColor" ref="613463035"/>
<nil key="IBUIHighlightedColor"/>
<int key="IBUIBaselineAdjustment">0</int>
Expand Down Expand Up @@ -602,6 +602,6 @@ cm5pYSAoKzEgNDA4LTk5Ni0xMDEwKSBuZXh0IHN1bmRheS4</string>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">1929</string>
<string key="IBCocoaTouchPluginVersion">1930</string>
</data>
</archive>
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,12 @@ -(void)fillDemoLabel
// now we only change the color of "FoodReporter"
[attrStr setTextColor:[UIColor colorWithRed:0.f green:0.f blue:0.5 alpha:1.f] range:[txt rangeOfString:@TXT_BOLD]];
[attrStr setTextBold:YES range:[txt rangeOfString:@TXT_BOLD]];

/**(2)** Affect the NSAttributedString to the OHAttributedLabel *******/
self.customLinkDemoLabel.attributedText = attrStr;

// and add a link to the "share your food!" text
[self.customLinkDemoLabel addCustomLink:[NSURL URLWithString:@"http://www.foodreporter.net"] inRange:[txt rangeOfString:@TXT_LINK]];
[attrStr setLink:[NSURL URLWithString:@"http://www.foodreporter.net"] range:[txt rangeOfString:@TXT_LINK]];

// "Hello World!" will be displayed in the label, justified, "Hello" in red and " World!" in gray.
/**(2)** Affect the NSAttributedString to the OHAttributedLabel *******/
self.customLinkDemoLabel.attributedText = attrStr;
}

-(IBAction)toggleBold:(UISwitch*)boldSwitch
Expand All @@ -88,9 +87,6 @@ -(IBAction)toggleBold:(UISwitch*)boldSwitch
[mas setTextBold:boldSwitch.on range:[plainText rangeOfString:@TXT_BOLD]];
// Affect back the attributed string to the label
self.customLinkDemoLabel.attributedText = mas;

// Restore the link (as each time we change the attributedText we remove custom links to avoid inconsistencies
[self.customLinkDemoLabel addCustomLink:[NSURL URLWithString:@"http://www.foodreporter.net"] inRange:[plainText rangeOfString:@TXT_LINK]];

#if ! __has_feature(objc_arc)
// Cleaning: balance the "mutableCopy" call with a "release"
Expand All @@ -111,16 +107,17 @@ -(void)configureMentionLabel
{
// Detect all "@xxx" mention-like strings using the "@\w+" regular expression
NSRegularExpression* userRegex = [NSRegularExpression regularExpressionWithPattern:@"@\\w+" options:0 error:nil];
NSMutableAttributedString* mas = [self.mentionDemoLabel.attributedText mutableCopy];
[userRegex enumerateMatchesInString:self.mentionDemoLabel.text options:0 range:NSMakeRange(0,self.mentionDemoLabel.text.length)
usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop)
{
// For each "@xxx" user mention found, add a custom link:
NSString* user = [[self.mentionDemoLabel.text substringWithRange:match.range] substringFromIndex:1]; // get the matched user name, removing the "@"
NSString* linkURLString = [NSString stringWithFormat:@"user:%@", user]; // build the "user:" link
[self.mentionDemoLabel addCustomLink:[NSURL URLWithString:linkURLString] inRange:match.range]; // add it
[mas setLink:[NSURL URLWithString:linkURLString] range:match.range]; // add it
}];

self.mentionDemoLabel.centerVertically = YES;
self.mentionDemoLabel.attributedText = mas;
self.mentionDemoLabel.centerVertically = YES;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ - (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self.sampleLabel addCustomLink:nil inRange:NSMakeRange(8,11)];
[self.sampleLabel addCustomLink:[NSURL URLWithString:@"#"] inRange:NSMakeRange(8,11)]; // Add fake link so we can see the UIAppearance effect
self.sampleLabel.centerVertically = YES;
}

Expand Down
5 changes: 5 additions & 0 deletions OHAttributedLabel/Source/NSAttributedString+Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#import <CoreText/CoreText.h>
#import <UIKit/UIKit.h>

extern NSString* kOHLinkAttributeName;

/////////////////////////////////////////////////////////////////////////////////////
#pragma mark - NSAttributedString Additions
Expand All @@ -51,6 +52,8 @@
-(BOOL)textIsBoldAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;
-(CTTextAlignment)textAlignmentAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;
-(CTLineBreakMode)lineBreakModeAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;

-(NSURL*)linkAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;
@end


Expand All @@ -74,6 +77,8 @@

-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode;
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode range:(NSRange)range;

-(void)setLink:(NSURL*)link range:(NSRange)range;
@end


17 changes: 17 additions & 0 deletions OHAttributedLabel/Source/NSAttributedString+Attributes.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#define MRC_AUTORELEASE(x) [(x) autorelease]
#endif

NSString* kOHLinkAttributeName = @"NSLinkAttributeName"; // Use the same value as OSX, to be compatible in case Apple port this to iOS one day too

/////////////////////////////////////////////////////////////////////////////////////
#pragma mark - NSAttributedString Additions

Expand Down Expand Up @@ -130,6 +132,12 @@ -(CTLineBreakMode)lineBreakModeAtIndex:(NSUInteger)index effectiveRange:(NSRange
CTParagraphStyleGetValueForSpecifier(style, kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreakMode);
return lineBreakMode;
}

-(NSURL*)linkAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange
{
return [self attribute:kOHLinkAttributeName atIndex:index effectiveRange:aRange];
}

@end


Expand Down Expand Up @@ -265,6 +273,15 @@ -(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMod
CFRelease(aStyle);
}

-(void)setLink:(NSURL*)link range:(NSRange)range
{
[self removeAttribute:kOHLinkAttributeName range:range]; // Work around for Apple leak
if (link)
{
[self addAttribute:kOHLinkAttributeName value:(BRIDGE_CAST id)link range:range];

This comment has been minimized.

Copy link
@peetonn

peetonn Nov 23, 2012

for some reason, this line yields error in my code: ".../OHAttributedLabel/OHAttributedLabel/Source/NSAttributedString+Attributes.m:281:55: Incompatible types casting 'NSURL *' to 'id' with a __bridge cast"
do you what could be possible reasons?

This comment has been minimized.

Copy link
@AliSoftware

AliSoftware Nov 23, 2012

Author Owner

Hi,

You probably didn't include OHAttributedLabel as a sibling project in your workspace (and linking with the generated OHAttributedLabel library) as explained in the README instructions and the wiki.

You should keep OHAttributedLabel in its own project and include this project in your Xcode 4's workspace, so that OHAttributedLabel uses its own compilation settings. I strongly recommend against copying the OHAttributedLabel source files directly into your application project (which you probably did if you have this error) because doing so will compile OHAttributedLabel source files using your own project settings (in your case with ARC turned on), leading to such compilation errors.

OHAttributedLabel should keep its own compilation settings regardless of your application's project settings (so it can work whether you have ARC turned ON or OFF in your app project or modified other project settings in your own project : your settings should be unrelated to the compilation of OHAttributedLabel itself), and Xcode 4's workspaces exists for that purpose so you should use them to avoid mixing compilation settings between OHAttributedLabel and your own app's project.

I will probably fix this line in a future commit anyway, to make OHAttributedLabel being able to compile using ARC if needed one day, but it is not required at all to use OHAttributedLabel in your project, even the ones using ARC, as long as you do follow the README instructions instead of copying the files directly in your app project.

}
}

@end


34 changes: 33 additions & 1 deletion OHAttributedLabel/Source/OHAttributedLabel.m
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,18 @@ -(void)recomputeLinksInTextIfNeeded
}
};

// Links set by text attribute
[_attributedText enumerateAttribute:kOHLinkAttributeName inRange:NSMakeRange(0, [_attributedText length])
options:0 usingBlock:^(id value, NSRange range, BOOL *stop)
{
if (value)
{
NSTextCheckingResult* result = [NSTextCheckingResult linkCheckingResultWithRange:range URL:(BRIDGE_CAST NSURL*)value];
applyLinkStyle(result);
}
}];

// Automatically Detected Links
if (plainText && (self.automaticallyAddLinksForType > 0))
{
[_linksDetector enumerateMatchesInString:plainText options:0 range:NSMakeRange(0,[plainText length])
Expand All @@ -277,6 +289,8 @@ -(void)recomputeLinksInTextIfNeeded
applyLinkStyle(result);
}];
}

// Custom Links
[_customLinks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
applyLinkStyle((NSTextCheckingResult*)obj);
Expand All @@ -298,8 +312,25 @@ -(NSTextCheckingResult*)linkAtCharacterIndex:(CFIndex)idx
@autoreleasepool
{
NSString* plainText = [_attributedText string];
if (plainText && (self.automaticallyAddLinksForType > 0))

// Links set by text attribute
if (_attributedText)
{
[_attributedText enumerateAttribute:kOHLinkAttributeName inRange:NSMakeRange(0, [_attributedText length])
options:0 usingBlock:^(id value, NSRange range, BOOL *stop)
{
if (value && NSLocationInRange(idx, range))
{
NSTextCheckingResult* result = [NSTextCheckingResult linkCheckingResultWithRange:range URL:(BRIDGE_CAST NSURL*)value];
foundResult = MRC_RETAIN(result);
*stop = YES;
}
}];
}

if (!foundResult && plainText && (self.automaticallyAddLinksForType > 0))
{
// Automatically Detected Links
[_linksDetector enumerateMatchesInString:plainText options:0 range:NSMakeRange(0,[plainText length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
{
Expand All @@ -314,6 +345,7 @@ -(NSTextCheckingResult*)linkAtCharacterIndex:(CFIndex)idx

if (!foundResult)
{
// Custom Links
[_customLinks enumerateObjectsUsingBlock:^(id obj, NSUInteger aidx, BOOL *stop)
{
NSRange r = [(NSTextCheckingResult*)obj range];
Expand Down
100 changes: 65 additions & 35 deletions OHAttributedLabel/TagParsers/OHASBasicHTMLParser.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,49 +21,79 @@ +(NSDictionary*)tagMappings
{
return [NSDictionary dictionaryWithObjectsAndKeys:

^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match) {
^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match)
{
NSRange textRange = [match rangeAtIndex:1];
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
if (textRange.length>0) [foundString setTextBold:YES range:NSMakeRange(0,textRange.length)];
return MRC_AUTORELEASE(foundString);
}, @"<b>(.*?)</b>",
if (textRange.length>0)
{
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
[foundString setTextBold:YES range:NSMakeRange(0,textRange.length)];
return MRC_AUTORELEASE(foundString);
} else {
return nil;
}
}, @"<b>(.+?)</b>",

^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match) {
^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match)
{
NSRange textRange = [match rangeAtIndex:1];
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
if (textRange.length>0) [foundString setTextIsUnderlined:YES];
return MRC_AUTORELEASE(foundString);
}, @"<u>(.*?)</u>",
if (textRange.length>0)
{
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
[foundString setTextIsUnderlined:YES];
return MRC_AUTORELEASE(foundString);
} else {
return nil;
}
}, @"<u>(.+?)</u>",

^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match) {
NSString* fontName = [str attributedSubstringFromRange:[match rangeAtIndex:2]].string;
CGFloat fontSize = [str attributedSubstringFromRange:[match rangeAtIndex:4]].string.floatValue;
^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match)
{
NSRange fontNameRange = [match rangeAtIndex:2];
NSRange fontSizeRange = [match rangeAtIndex:4];
NSRange textRange = [match rangeAtIndex:5];
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
if (textRange.length>0) [foundString setFontName:fontName size:fontSize];
return MRC_AUTORELEASE(foundString);
}, @"<font name=(['\"])(.*?)\\1 size=(['\"])(.*?)\\3>(.*?)</font>",
if ((fontNameRange.length>0) && (fontSizeRange.length>0) && (textRange.length>0))
{
NSString* fontName = [str attributedSubstringFromRange:fontNameRange].string;
CGFloat fontSize = [str attributedSubstringFromRange:fontSizeRange].string.floatValue;
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
[foundString setFontName:fontName size:fontSize];
return MRC_AUTORELEASE(foundString);
} else {
return nil;
}
}, @"<font name=(['\"])(.+?)\\1 size=(['\"])(.+?)\\3>(.+?)</font>",

^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match) {
NSString* colorName = [str attributedSubstringFromRange:[match rangeAtIndex:2]].string;
UIColor* color = UIColorFromString(colorName);
^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match)
{
NSRange colorRange = [match rangeAtIndex:2];
NSRange textRange = [match rangeAtIndex:3];
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
if (textRange.length>0) [foundString setTextColor:color];
return MRC_AUTORELEASE(foundString);
}, @"<font color=(['\"])(.*?)\\1>(.*?)</font>",
if ((colorRange.length>0) && (textRange.length>0))
{
NSString* colorName = [str attributedSubstringFromRange:colorRange].string;
UIColor* color = UIColorFromString(colorName);
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
[foundString setTextColor:color];
return MRC_AUTORELEASE(foundString);
} else {
return nil;
}
}, @"<font color=(['\"])(.+?)\\1>(.+?)</font>",

/*
// Disabled for now as there is no official CoreText attribute name to define links.
// To be able to do this, we have implement a custom attribute ourselves and add support for it in OHAttributedLabel
^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match) {
NSString* link = [str attributedSubstringFromRange:[match rangeAtIndex:1]].string;
NSRange textRange = [match rangeAtIndex:2];
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
[foundString addAttribute:@"NSLinkAttributeName" value:link range:NSMakeRange(0,textRange.length)];
return [foundString autorelease];
}, @"<a href='(.*?)'>(.*?)</a>",
*/
^NSAttributedString*(NSAttributedString* str, NSTextCheckingResult* match)
{
NSRange linkRange = [match rangeAtIndex:2];
NSRange textRange = [match rangeAtIndex:3];
if ((linkRange.length>0) && (textRange.length>0))
{
NSString* link = [str attributedSubstringFromRange:linkRange].string;
NSMutableAttributedString* foundString = [[str attributedSubstringFromRange:textRange] mutableCopy];
[foundString setLink:[NSURL URLWithString:link] range:NSMakeRange(0,textRange.length)];
return [foundString autorelease];
} else {
return nil;
}
}, @"<a href=(['\"])(.+?)\\1>(.+?)</a>",

nil];
}
Expand Down
Loading

1 comment on commit 78b2ff4

@runmad
Copy link

@runmad runmad commented on 78b2ff4 Nov 14, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lazy question here, but does anyone know if you can store the NSAttributedString to Core Data and retain the links?

Please sign in to comment.