mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-19 12:58:29 +00:00
Merge branch 'ios6'
* ios6: Major improvements to activities/interactions. Adding saved story cells. Fixing loading/finding story HUDs for both ipad and iphone. Fixing interactions/activities sizing on ipad. Fixing layout on interactions/activities. Also fixing major bug around loading feeds pausing due to synchronous avatar image fetch. Removing OHAttributedLabel, but the new attributed label sucks.
This commit is contained in:
commit
cde48d8a8e
18 changed files with 210 additions and 1156 deletions
|
@ -7,10 +7,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "OHAttributedLabel.h"
|
|
||||||
|
|
||||||
@interface ActivityCell : UITableViewCell {
|
@interface ActivityCell : UITableViewCell {
|
||||||
OHAttributedLabel *activityLabel;
|
UILabel *activityLabel;
|
||||||
UIImageView *faviconView;
|
UIImageView *faviconView;
|
||||||
int topMargin;
|
int topMargin;
|
||||||
int bottomMargin;
|
int bottomMargin;
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
int avatarSize;
|
int avatarSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (nonatomic, strong) OHAttributedLabel *activityLabel;
|
@property (nonatomic, strong) UILabel *activityLabel;
|
||||||
@property (nonatomic, strong) UIImageView *faviconView;
|
@property (nonatomic, strong) UIImageView *faviconView;
|
||||||
@property (readwrite) int topMargin;
|
@property (readwrite) int topMargin;
|
||||||
@property (readwrite) int bottomMargin;
|
@property (readwrite) int bottomMargin;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "ActivityCell.h"
|
#import "ActivityCell.h"
|
||||||
#import "NSAttributedString+Attributes.h"
|
|
||||||
#import "UIImageView+AFNetworking.h"
|
#import "UIImageView+AFNetworking.h"
|
||||||
|
|
||||||
@implementation ActivityCell
|
@implementation ActivityCell
|
||||||
|
@ -31,9 +30,8 @@
|
||||||
self.faviconView = favicon;
|
self.faviconView = favicon;
|
||||||
[self.contentView addSubview:favicon];
|
[self.contentView addSubview:favicon];
|
||||||
|
|
||||||
OHAttributedLabel *activity = [[OHAttributedLabel alloc] initWithFrame:CGRectZero];
|
UILabel *activity = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||||
activity.backgroundColor = [UIColor whiteColor];
|
activity.backgroundColor = [UIColor whiteColor];
|
||||||
activity.automaticallyAddLinksForType = NO;
|
|
||||||
self.activityLabel = activity;
|
self.activityLabel = activity;
|
||||||
[self.contentView addSubview:activity];
|
[self.contentView addSubview:activity];
|
||||||
|
|
||||||
|
@ -61,13 +59,16 @@
|
||||||
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
|
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
|
||||||
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
|
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
|
||||||
self.activityLabel.frame = labelRect;
|
self.activityLabel.frame = labelRect;
|
||||||
|
[self.activityLabel sizeToFit];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int)setActivity:(NSDictionary *)activity withUserProfile:(NSDictionary *)userProfile withWidth:(int)width {
|
- (int)setActivity:(NSDictionary *)activity withUserProfile:(NSDictionary *)userProfile withWidth:(int)width {
|
||||||
// must set the height again for dynamic height in heightForRowAtIndexPath in
|
// must set the height again for dynamic height in heightForRowAtIndexPath in
|
||||||
CGRect activityLabelRect = self.activityLabel.bounds;
|
CGRect activityLabelRect = self.activityLabel.bounds;
|
||||||
activityLabelRect.size.width = width - leftMargin - avatarSize - leftMargin - rightMargin;
|
activityLabelRect.size.width = width - leftMargin - avatarSize - leftMargin - rightMargin;
|
||||||
|
|
||||||
self.activityLabel.frame = activityLabelRect;
|
self.activityLabel.frame = activityLabelRect;
|
||||||
|
self.activityLabel.numberOfLines = 0;
|
||||||
self.faviconView.frame = CGRectMake(leftMargin, topMargin, avatarSize, avatarSize);
|
self.faviconView.frame = CGRectMake(leftMargin, topMargin, avatarSize, avatarSize);
|
||||||
|
|
||||||
NSString *category = [activity objectForKey:@"category"];
|
NSString *category = [activity objectForKey:@"category"];
|
||||||
|
@ -120,15 +121,15 @@
|
||||||
txt = [NSString stringWithFormat:@"%@ followed %@.", username, withUserUsername];
|
txt = [NSString stringWithFormat:@"%@ followed %@.", username, withUserUsername];
|
||||||
} else if ([category isEqualToString:@"comment_reply"]) {
|
} else if ([category isEqualToString:@"comment_reply"]) {
|
||||||
withUserUsername = [[activity objectForKey:@"with_user"] objectForKey:@"username"];
|
withUserUsername = [[activity objectForKey:@"with_user"] objectForKey:@"username"];
|
||||||
txt = [NSString stringWithFormat:@"%@ replied to %@: \n%@", username, withUserUsername, comment];
|
txt = [NSString stringWithFormat:@"%@ replied to %@: \n \n%@", username, withUserUsername, comment];
|
||||||
} else if ([category isEqualToString:@"comment_like"]) {
|
} else if ([category isEqualToString:@"comment_like"]) {
|
||||||
withUserUsername = [[activity objectForKey:@"with_user"] objectForKey:@"username"];
|
withUserUsername = [[activity objectForKey:@"with_user"] objectForKey:@"username"];
|
||||||
txt = [NSString stringWithFormat:@"%@ favorited %@'s comment on %@:\n%@", username, withUserUsername, title, comment];
|
txt = [NSString stringWithFormat:@"%@ favorited %@'s comment on %@:\n \n%@", username, withUserUsername, title, comment];
|
||||||
} else if ([category isEqualToString:@"sharedstory"]) {
|
} else if ([category isEqualToString:@"sharedstory"]) {
|
||||||
if ([content class] == [NSNull class] || [content isEqualToString:@""] || content == nil) {
|
if ([content class] == [NSNull class] || [content isEqualToString:@""] || content == nil) {
|
||||||
txt = [NSString stringWithFormat:@"%@ shared %@.", username, title];
|
txt = [NSString stringWithFormat:@"%@ shared %@.", username, title];
|
||||||
} else {
|
} else {
|
||||||
txt = [NSString stringWithFormat:@"%@ shared %@:\n%@", username, title, comment];
|
txt = [NSString stringWithFormat:@"%@ shared %@:\n \n%@", username, title, comment];
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ([category isEqualToString:@"star"]) {
|
} else if ([category isEqualToString:@"star"]) {
|
||||||
|
@ -139,38 +140,60 @@
|
||||||
txt = [NSString stringWithFormat:@"You signed up for NewsBlur."];
|
txt = [NSString stringWithFormat:@"You signed up for NewsBlur."];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *txtWithTime = [NSString stringWithFormat:@"%@\n%@", txt, time];
|
NSString *txtWithTime = [NSString stringWithFormat:@"%@\n \n%@", txt, time];
|
||||||
NSMutableAttributedString* attrStr = [NSMutableAttributedString attributedStringWithString:txtWithTime];
|
NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString:txtWithTime];
|
||||||
|
|
||||||
// for those calls we don't specify a range so it affects the whole string
|
[attrStr setAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Helvetica" size:13]} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
[attrStr setFont:[UIFont fontWithName:@"Helvetica" size:14]];
|
if (self.highlighted) {
|
||||||
[attrStr setTextColor:UIColorFromRGB(0x333333)];
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0xffffff)} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
|
} else {
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x333333)} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
|
}
|
||||||
|
|
||||||
if (![username isEqualToString:@"You"]){
|
if (![username isEqualToString:@"You"]){
|
||||||
[attrStr setTextColor:UIColorFromRGB(NEWSBLUR_LINK_COLOR) range:[txtWithTime rangeOfString:username]];
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(NEWSBLUR_LINK_COLOR)} range:[txtWithTime rangeOfString:username]];
|
||||||
[attrStr setTextBold:YES range:[txt rangeOfString:username]];
|
[attrStr addAttributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:13]} range:[txtWithTime rangeOfString:username]];
|
||||||
|
}
|
||||||
|
if (withUserUsername.length) {
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(NEWSBLUR_LINK_COLOR)} range:[txtWithTime rangeOfString:withUserUsername]];
|
||||||
|
[attrStr addAttributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:13]} range:[txtWithTime rangeOfString:withUserUsername]];
|
||||||
|
}
|
||||||
|
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(NEWSBLUR_LINK_COLOR)} range:[txtWithTime rangeOfString:title]];
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x666666)} range:[txtWithTime rangeOfString:comment]];
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x999999)} range:[txtWithTime rangeOfString:time]];
|
||||||
|
[attrStr addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Helvetica" size:11]} range:[txtWithTime rangeOfString:time]];
|
||||||
|
NSMutableParagraphStyle* style= [NSMutableParagraphStyle new];
|
||||||
|
style.lineBreakMode = NSLineBreakByWordWrapping;
|
||||||
|
[attrStr addAttributes:@{NSParagraphStyleAttributeName: style} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
|
|
||||||
|
NSRange commentRange = [txtWithTime rangeOfString:comment];
|
||||||
|
if (commentRange.location != NSNotFound) {
|
||||||
|
NSLog(@"Spacing: %@", comment);
|
||||||
|
commentRange.location -= 2;
|
||||||
|
commentRange.length = 1;
|
||||||
|
if ([[txtWithTime substringWithRange:commentRange] isEqualToString:@" "]) {
|
||||||
|
[attrStr addAttribute:NSFontAttributeName
|
||||||
|
value:[UIFont systemFontOfSize:4.0f]
|
||||||
|
range:commentRange];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[attrStr setTextColor:UIColorFromRGB(NEWSBLUR_LINK_COLOR) range:[txtWithTime rangeOfString:title]];
|
NSRange dateRange = [txtWithTime rangeOfString:time];
|
||||||
|
if (dateRange.location != NSNotFound) {
|
||||||
if(withUserUsername.length) {
|
dateRange.location -= 2;
|
||||||
[attrStr setTextColor:UIColorFromRGB(NEWSBLUR_LINK_COLOR) range:[txtWithTime rangeOfString:withUserUsername]];
|
dateRange.length = 1;
|
||||||
[attrStr setTextBold:YES range:[txtWithTime rangeOfString:withUserUsername]];
|
[attrStr addAttribute:NSFontAttributeName
|
||||||
|
value:[UIFont systemFontOfSize:4.0f]
|
||||||
|
range:dateRange];
|
||||||
}
|
}
|
||||||
|
|
||||||
[attrStr setTextColor:UIColorFromRGB(0x666666) range:[txtWithTime rangeOfString:comment]];
|
|
||||||
|
|
||||||
[attrStr setTextColor:UIColorFromRGB(0x999999) range:[txtWithTime rangeOfString:time]];
|
|
||||||
[attrStr setFont:[UIFont fontWithName:@"Helvetica" size:10] range:[txtWithTime rangeOfString:time]];
|
|
||||||
[attrStr setTextAlignment:kCTLeftTextAlignment lineBreakMode:kCTLineBreakByWordWrapping lineHeight:4];
|
|
||||||
|
|
||||||
self.activityLabel.attributedText = attrStr;
|
self.activityLabel.attributedText = attrStr;
|
||||||
|
|
||||||
[self.activityLabel sizeToFit];
|
[self.activityLabel sizeToFit];
|
||||||
|
|
||||||
int height = self.activityLabel.frame.size.height;
|
int height = self.activityLabel.frame.size.height;
|
||||||
|
|
||||||
return MAX(height, self.faviconView.frame.size.height);
|
return MAX(height + topMargin + bottomMargin, self.faviconView.frame.size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)stripFormatting:(NSString *)str {
|
- (NSString *)stripFormatting:(NSString *)str {
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
@synthesize pageFinished;
|
@synthesize pageFinished;
|
||||||
@synthesize activitiesPage;
|
@synthesize activitiesPage;
|
||||||
|
|
||||||
#define MINIMUM_ACTIVITY_HEIGHT 48 + 30
|
#define MINIMUM_ACTIVITY_HEIGHT_IPAD 78
|
||||||
|
#define MINIMUM_ACTIVITY_HEIGHT_IPHONE 54
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame
|
- (id)initWithFrame:(CGRect)frame
|
||||||
{
|
{
|
||||||
|
@ -173,7 +174,11 @@
|
||||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
int activitiesCount = [appDelegate.userActivitiesArray count];
|
int activitiesCount = [appDelegate.userActivitiesArray count];
|
||||||
if (indexPath.row >= activitiesCount) {
|
if (indexPath.row >= activitiesCount) {
|
||||||
return MINIMUM_ACTIVITY_HEIGHT;
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||||
|
return MINIMUM_ACTIVITY_HEIGHT_IPAD;
|
||||||
|
} else {
|
||||||
|
return MINIMUM_ACTIVITY_HEIGHT_IPHONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id activityCell;
|
id activityCell;
|
||||||
|
@ -189,7 +194,7 @@
|
||||||
int height = [activityCell setActivity:[appDelegate.userActivitiesArray
|
int height = [activityCell setActivity:[appDelegate.userActivitiesArray
|
||||||
objectAtIndex:(indexPath.row)]
|
objectAtIndex:(indexPath.row)]
|
||||||
withUserProfile:userProfile
|
withUserProfile:userProfile
|
||||||
withWidth:self.frame.size.width - 20] + 30;
|
withWidth:self.frame.size.width - 20];
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
|
|
||||||
|
@ -223,8 +228,7 @@
|
||||||
NSString *category = [activitiy objectForKey:@"category"];
|
NSString *category = [activitiy objectForKey:@"category"];
|
||||||
if ([category isEqualToString:@"follow"]) {
|
if ([category isEqualToString:@"follow"]) {
|
||||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||||
} else if ([category isEqualToString:@"star"] ||
|
} else if ([category isEqualToString:@"signup"]){
|
||||||
[category isEqualToString:@"signup"]){
|
|
||||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||||
} else {
|
} else {
|
||||||
|
@ -283,6 +287,12 @@
|
||||||
withUser:[activity objectForKey:@"with_user"]
|
withUser:[activity objectForKey:@"with_user"]
|
||||||
showFindingStory:YES];
|
showFindingStory:YES];
|
||||||
appDelegate.tryFeedCategory = category;
|
appDelegate.tryFeedCategory = category;
|
||||||
|
} else if ([category isEqualToString:@"star"]) {
|
||||||
|
NSString *contentIdStr = [NSString stringWithFormat:@"%@",
|
||||||
|
[activity objectForKey:@"content_id"]];
|
||||||
|
[appDelegate loadStarredDetailViewWithStory:contentIdStr
|
||||||
|
showFindingStory:YES];
|
||||||
|
appDelegate.tryFeedCategory = category;
|
||||||
} else if ([category isEqualToString:@"feedsub"]) {
|
} else if ([category isEqualToString:@"feedsub"]) {
|
||||||
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
|
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
|
||||||
[activity objectForKey:@"feed_id"]];
|
[activity objectForKey:@"feed_id"]];
|
||||||
|
@ -310,7 +320,12 @@
|
||||||
if (self.pageFinished) {
|
if (self.pageFinished) {
|
||||||
UIImage *img = [UIImage imageNamed:@"fleuron.png"];
|
UIImage *img = [UIImage imageNamed:@"fleuron.png"];
|
||||||
UIImageView *fleuron = [[UIImageView alloc] initWithImage:img];
|
UIImageView *fleuron = [[UIImageView alloc] initWithImage:img];
|
||||||
int height = MINIMUM_ACTIVITY_HEIGHT;
|
int height;
|
||||||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||||
|
height = MINIMUM_ACTIVITY_HEIGHT_IPAD;
|
||||||
|
} else {
|
||||||
|
height = MINIMUM_ACTIVITY_HEIGHT_IPHONE;
|
||||||
|
}
|
||||||
|
|
||||||
fleuron.frame = CGRectMake(0, 0, self.frame.size.width, height);
|
fleuron.frame = CGRectMake(0, 0, self.frame.size.width, height);
|
||||||
fleuron.contentMode = UIViewContentModeCenter;
|
fleuron.contentMode = UIViewContentModeCenter;
|
||||||
|
|
|
@ -787,6 +787,7 @@
|
||||||
FeedDetailTableCell *cell = (FeedDetailTableCell *)[self.storyTitlesTable cellForRowAtIndexPath:indexPath];
|
FeedDetailTableCell *cell = (FeedDetailTableCell *)[self.storyTitlesTable cellForRowAtIndexPath:indexPath];
|
||||||
[self loadStory:cell atRow:indexPath.row];
|
[self loadStory:cell atRow:indexPath.row];
|
||||||
|
|
||||||
|
[MBProgressHUD hideHUDForView:self.view animated:YES];
|
||||||
// found the story, reset the two flags.
|
// found the story, reset the two flags.
|
||||||
// appDelegate.tryFeedStoryId = nil;
|
// appDelegate.tryFeedStoryId = nil;
|
||||||
appDelegate.inFindingStoryMode = NO;
|
appDelegate.inFindingStoryMode = NO;
|
||||||
|
@ -1070,48 +1071,7 @@
|
||||||
[appDelegate calculateStoryLocations];
|
[appDelegate calculateStoryLocations];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i < appDelegate.storyLocationsCount; i++) {
|
[self.storyTitlesTable reloadData];
|
||||||
int location = [[[appDelegate activeFeedStoryLocations] objectAtIndex:i] intValue];
|
|
||||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
|
|
||||||
NSDictionary *story = [appDelegate.activeFeedStories objectAtIndex:location];
|
|
||||||
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
|
|
||||||
|
|
||||||
if (previousLevel == -1) {
|
|
||||||
if (newLevel == 0 && score == -1) {
|
|
||||||
[deleteIndexPaths addObject:indexPath];
|
|
||||||
} else if (newLevel == 1 && score < 1) {
|
|
||||||
[deleteIndexPaths addObject:indexPath];
|
|
||||||
}
|
|
||||||
} else if (previousLevel == 0) {
|
|
||||||
if (newLevel == -1 && score == -1) {
|
|
||||||
[insertIndexPaths addObject:indexPath];
|
|
||||||
} else if (newLevel == 1 && score == 0) {
|
|
||||||
[deleteIndexPaths addObject:indexPath];
|
|
||||||
}
|
|
||||||
} else if (previousLevel == 1) {
|
|
||||||
if (newLevel == 0 && score == 0) {
|
|
||||||
[insertIndexPaths addObject:indexPath];
|
|
||||||
} else if (newLevel == -1 && score < 1) {
|
|
||||||
[insertIndexPaths addObject:indexPath];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newLevel > previousLevel) {
|
|
||||||
[appDelegate setSelectedIntelligence:newLevel];
|
|
||||||
[appDelegate calculateStoryLocations];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self.storyTitlesTable beginUpdates];
|
|
||||||
if ([deleteIndexPaths count] > 0) {
|
|
||||||
[self.storyTitlesTable deleteRowsAtIndexPaths:deleteIndexPaths
|
|
||||||
withRowAnimation:UITableViewRowAnimationNone];
|
|
||||||
}
|
|
||||||
if ([insertIndexPaths count] > 0) {
|
|
||||||
[self.storyTitlesTable insertRowsAtIndexPaths:insertIndexPaths
|
|
||||||
withRowAnimation:UITableViewRowAnimationNone];
|
|
||||||
}
|
|
||||||
[self.storyTitlesTable endUpdates];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow {
|
- (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow {
|
||||||
|
|
|
@ -7,10 +7,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "OHAttributedLabel.h"
|
|
||||||
|
|
||||||
@interface InteractionCell : UITableViewCell {
|
@interface InteractionCell : UITableViewCell {
|
||||||
OHAttributedLabel *interactionLabel;
|
UILabel *interactionLabel;
|
||||||
UIImageView *avatarView;
|
UIImageView *avatarView;
|
||||||
int topMargin;
|
int topMargin;
|
||||||
int bottomMargin;
|
int bottomMargin;
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
int avatarSize;
|
int avatarSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (retain, nonatomic) OHAttributedLabel *interactionLabel;
|
@property (retain, nonatomic) UILabel *interactionLabel;
|
||||||
@property (retain, nonatomic) UIImageView *avatarView;
|
@property (retain, nonatomic) UIImageView *avatarView;
|
||||||
@property (readwrite) int topMargin;
|
@property (readwrite) int topMargin;
|
||||||
@property (readwrite) int bottomMargin;
|
@property (readwrite) int bottomMargin;
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "InteractionCell.h"
|
#import "InteractionCell.h"
|
||||||
#import "NSAttributedString+Attributes.h"
|
|
||||||
#import "UIImageView+AFNetworking.h"
|
#import "UIImageView+AFNetworking.h"
|
||||||
|
#import <CoreText/CoreText.h>
|
||||||
|
|
||||||
@implementation InteractionCell
|
@implementation InteractionCell
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@
|
||||||
self.avatarView = avatar;
|
self.avatarView = avatar;
|
||||||
[self.contentView addSubview:avatar];
|
[self.contentView addSubview:avatar];
|
||||||
|
|
||||||
OHAttributedLabel *interaction = [[OHAttributedLabel alloc] initWithFrame:CGRectZero];
|
UILabel *interaction = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||||
interaction.backgroundColor = [UIColor whiteColor];
|
interaction.backgroundColor = [UIColor whiteColor];
|
||||||
interaction.automaticallyAddLinksForType = NO;
|
// interaction.automaticallyAddLinksForType = NO;
|
||||||
self.interactionLabel = interaction;
|
self.interactionLabel = interaction;
|
||||||
[self.contentView addSubview:interaction];
|
[self.contentView addSubview:interaction];
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@
|
||||||
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
|
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
|
||||||
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
|
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
|
||||||
self.interactionLabel.frame = labelRect;
|
self.interactionLabel.frame = labelRect;
|
||||||
|
[self.interactionLabel sizeToFit];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,17 +75,16 @@
|
||||||
interactionLabelRect.size.height = 300;
|
interactionLabelRect.size.height = 300;
|
||||||
|
|
||||||
self.interactionLabel.frame = interactionLabelRect;
|
self.interactionLabel.frame = interactionLabelRect;
|
||||||
|
self.interactionLabel.numberOfLines = 0;
|
||||||
self.avatarView.frame = CGRectMake(leftMargin, topMargin, avatarSize, avatarSize);
|
self.avatarView.frame = CGRectMake(leftMargin, topMargin, avatarSize, avatarSize);
|
||||||
|
|
||||||
// UIImage *placeholder = [UIImage imageNamed:@"user_light"];
|
|
||||||
|
|
||||||
// this is for the rare instance when the with_user doesn't return anything
|
// this is for the rare instance when the with_user doesn't return anything
|
||||||
if ([[interaction objectForKey:@"with_user"] class] == [NSNull class]) {
|
if ([[interaction objectForKey:@"with_user"] class] == [NSNull class]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self.avatarView setImageWithURL:[NSURL URLWithString:[[interaction objectForKey:@"with_user"] objectForKey:@"photo_url"]]
|
[self.avatarView setImageWithURL:[NSURL URLWithString:[[interaction objectForKey:@"with_user"] objectForKey:@"photo_url"]]
|
||||||
placeholderImage:nil];
|
placeholderImage:nil ];
|
||||||
|
|
||||||
NSString *category = [interaction objectForKey:@"category"];
|
NSString *category = [interaction objectForKey:@"category"];
|
||||||
NSString *content = [interaction objectForKey:@"content"];
|
NSString *content = [interaction objectForKey:@"content"];
|
||||||
|
@ -97,50 +97,72 @@
|
||||||
if ([category isEqualToString:@"follow"]) {
|
if ([category isEqualToString:@"follow"]) {
|
||||||
txt = [NSString stringWithFormat:@"%@ is now following you.", username];
|
txt = [NSString stringWithFormat:@"%@ is now following you.", username];
|
||||||
} else if ([category isEqualToString:@"comment_reply"]) {
|
} else if ([category isEqualToString:@"comment_reply"]) {
|
||||||
txt = [NSString stringWithFormat:@"%@ replied to your comment on %@:\n%@", username, title, comment];
|
txt = [NSString stringWithFormat:@"%@ replied to your comment on %@:\n \n%@", username, title, comment];
|
||||||
} else if ([category isEqualToString:@"reply_reply"]) {
|
} else if ([category isEqualToString:@"reply_reply"]) {
|
||||||
txt = [NSString stringWithFormat:@"%@ replied to your reply on %@:\n%@", username, title, comment];
|
txt = [NSString stringWithFormat:@"%@ replied to your reply on %@:\n \n%@", username, title, comment];
|
||||||
} else if ([category isEqualToString:@"story_reshare"]) {
|
} else if ([category isEqualToString:@"story_reshare"]) {
|
||||||
if ([content isEqualToString:@""] || content == nil) {
|
if ([content isEqualToString:@""] || content == nil) {
|
||||||
txt = [NSString stringWithFormat:@"%@ re-shared %@.", username, title];
|
txt = [NSString stringWithFormat:@"%@ re-shared %@.", username, title];
|
||||||
} else {
|
} else {
|
||||||
txt = [NSString stringWithFormat:@"%@ re-shared %@:\n%@", username, title, comment];
|
txt = [NSString stringWithFormat:@"%@ re-shared %@:\n \n%@", username, title, comment];
|
||||||
}
|
}
|
||||||
} else if ([category isEqualToString:@"comment_like"]) {
|
} else if ([category isEqualToString:@"comment_like"]) {
|
||||||
txt = [NSString stringWithFormat:@"%@ favorited your comments on %@:\n%@", username, title, comment];
|
txt = [NSString stringWithFormat:@"%@ favorited your comments on %@:\n \n%@", username, title, comment];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *txtWithTime = [NSString stringWithFormat:@"%@\n%@", txt, time];
|
NSString *txtWithTime = [NSString stringWithFormat:@"%@\n \n%@", txt, time];
|
||||||
NSMutableAttributedString* attrStr = [NSMutableAttributedString attributedStringWithString:txtWithTime];
|
NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString:txtWithTime];
|
||||||
|
|
||||||
// for those calls we don't specify a range so it affects the whole string
|
NSMutableParagraphStyle* style = [NSMutableParagraphStyle new];
|
||||||
[attrStr setFont:[UIFont fontWithName:@"Helvetica" size:14]];
|
style.lineBreakMode = NSLineBreakByWordWrapping;
|
||||||
|
style.alignment = NSTextAlignmentLeft;
|
||||||
|
style.lineSpacing = 1.0f;
|
||||||
|
[attrStr setAttributes:@{NSParagraphStyleAttributeName: style} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
|
|
||||||
|
[attrStr addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Helvetica" size:13]} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
if (self.highlighted) {
|
if (self.highlighted) {
|
||||||
[attrStr setTextColor:UIColorFromRGB(0xffffff)];
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0xffffff)} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
} else {
|
} else {
|
||||||
[attrStr setTextColor:UIColorFromRGB(0x333333)];
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x333333)} range:NSMakeRange(0, [txtWithTime length])];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![username isEqualToString:@"You"]){
|
if (![username isEqualToString:@"You"]){
|
||||||
[attrStr setTextColor:UIColorFromRGB(NEWSBLUR_LINK_COLOR) range:[txtWithTime rangeOfString:username]];
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(NEWSBLUR_LINK_COLOR)} range:[txtWithTime rangeOfString:username]];
|
||||||
[attrStr setTextBold:YES range:[txt rangeOfString:username]];
|
[attrStr addAttributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:13]} range:[txtWithTime rangeOfString:username]];
|
||||||
|
}
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(NEWSBLUR_LINK_COLOR)} range:[txtWithTime rangeOfString:title]];
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x666666)} range:[txtWithTime rangeOfString:comment]];
|
||||||
|
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x999999)} range:[txtWithTime rangeOfString:time]];
|
||||||
|
|
||||||
|
[attrStr addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Helvetica" size:11]} range:[txtWithTime rangeOfString:time]];
|
||||||
|
|
||||||
|
NSRange commentRange = [txtWithTime rangeOfString:comment];
|
||||||
|
if (commentRange.location != NSNotFound) {
|
||||||
|
commentRange.location -= 2;
|
||||||
|
commentRange.length = 1;
|
||||||
|
if ([[txtWithTime substringWithRange:commentRange] isEqualToString:@" "]) {
|
||||||
|
[attrStr addAttribute:NSFontAttributeName
|
||||||
|
value:[UIFont systemFontOfSize:6.0f]
|
||||||
|
range:commentRange];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[attrStr setTextColor:UIColorFromRGB(NEWSBLUR_LINK_COLOR) range:[txtWithTime rangeOfString:title]];
|
NSRange dateRange = [txtWithTime rangeOfString:time];
|
||||||
[attrStr setTextColor:UIColorFromRGB(0x666666) range:[txtWithTime rangeOfString:comment]];
|
if (dateRange.location != NSNotFound) {
|
||||||
|
dateRange.location -= 2;
|
||||||
[attrStr setTextColor:UIColorFromRGB(0x999999) range:[txtWithTime rangeOfString:time]];
|
dateRange.length = 1;
|
||||||
[attrStr setFont:[UIFont fontWithName:@"Helvetica" size:10] range:[txtWithTime rangeOfString:time]];
|
[attrStr addAttribute:NSFontAttributeName
|
||||||
[attrStr setTextAlignment:kCTLeftTextAlignment lineBreakMode:kCTLineBreakByWordWrapping lineHeight:4];
|
value:[UIFont systemFontOfSize:6.0f]
|
||||||
|
range:dateRange];
|
||||||
self.interactionLabel.attributedText = attrStr;
|
}
|
||||||
|
|
||||||
|
self.interactionLabel.attributedText = attrStr;
|
||||||
[self.interactionLabel sizeToFit];
|
[self.interactionLabel sizeToFit];
|
||||||
|
|
||||||
int height = self.interactionLabel.frame.size.height;
|
int height = self.interactionLabel.frame.size.height;
|
||||||
|
|
||||||
return height;
|
return height + topMargin + bottomMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)stripFormatting:(NSString *)str {
|
- (NSString *)stripFormatting:(NSString *)str {
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
#import "ASIHTTPRequest.h"
|
#import "ASIHTTPRequest.h"
|
||||||
#import "UserProfileViewController.h"
|
#import "UserProfileViewController.h"
|
||||||
|
|
||||||
#define MINIMUM_INTERACTION_HEIGHT 78
|
#define MINIMUM_INTERACTION_HEIGHT_IPAD 78
|
||||||
|
#define MINIMUM_INTERACTION_HEIGHT_IPHONE 54
|
||||||
|
|
||||||
@implementation InteractionsModule
|
@implementation InteractionsModule
|
||||||
|
|
||||||
|
@ -175,8 +176,15 @@
|
||||||
|
|
||||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
int userInteractions = [appDelegate.userInteractionsArray count];
|
int userInteractions = [appDelegate.userInteractionsArray count];
|
||||||
|
int minimumHeight;
|
||||||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||||
|
minimumHeight = MINIMUM_INTERACTION_HEIGHT_IPAD;
|
||||||
|
} else {
|
||||||
|
minimumHeight = MINIMUM_INTERACTION_HEIGHT_IPHONE;
|
||||||
|
}
|
||||||
|
|
||||||
if (indexPath.row >= userInteractions) {
|
if (indexPath.row >= userInteractions) {
|
||||||
return MINIMUM_INTERACTION_HEIGHT;
|
return minimumHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractionCell *interactionCell;
|
InteractionCell *interactionCell;
|
||||||
|
@ -185,9 +193,9 @@
|
||||||
} else {
|
} else {
|
||||||
interactionCell = [[SmallInteractionCell alloc] init];
|
interactionCell = [[SmallInteractionCell alloc] init];
|
||||||
}
|
}
|
||||||
int height = [interactionCell setInteraction:[appDelegate.userInteractionsArray objectAtIndex:(indexPath.row)] withWidth:self.frame.size.width - 20] + 30;
|
int height = [interactionCell setInteraction:[appDelegate.userInteractionsArray objectAtIndex:(indexPath.row)] withWidth:self.frame.size.width - 20];
|
||||||
if (height < MINIMUM_INTERACTION_HEIGHT) {
|
if (height < minimumHeight) {
|
||||||
return MINIMUM_INTERACTION_HEIGHT;
|
return minimumHeight;
|
||||||
} else {
|
} else {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
@ -230,6 +238,7 @@
|
||||||
|
|
||||||
// update the cell information
|
// update the cell information
|
||||||
[cell setInteraction:interaction withWidth: self.frame.size.width - 20];
|
[cell setInteraction:interaction withWidth: self.frame.size.width - 20];
|
||||||
|
[cell layoutSubviews];
|
||||||
}
|
}
|
||||||
|
|
||||||
return cell;
|
return cell;
|
||||||
|
@ -289,7 +298,13 @@
|
||||||
if (self.pageFinished) {
|
if (self.pageFinished) {
|
||||||
UIImage *img = [UIImage imageNamed:@"fleuron.png"];
|
UIImage *img = [UIImage imageNamed:@"fleuron.png"];
|
||||||
UIImageView *fleuron = [[UIImageView alloc] initWithImage:img];
|
UIImageView *fleuron = [[UIImageView alloc] initWithImage:img];
|
||||||
int height = MINIMUM_INTERACTION_HEIGHT;
|
|
||||||
|
int height;
|
||||||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||||
|
height = MINIMUM_INTERACTION_HEIGHT_IPAD;
|
||||||
|
} else {
|
||||||
|
height = MINIMUM_INTERACTION_HEIGHT_IPHONE;
|
||||||
|
}
|
||||||
|
|
||||||
fleuron.frame = CGRectMake(0, 0, self.frame.size.width, height);
|
fleuron.frame = CGRectMake(0, 0, self.frame.size.width, height);
|
||||||
fleuron.contentMode = UIViewContentModeCenter;
|
fleuron.contentMode = UIViewContentModeCenter;
|
||||||
|
|
|
@ -270,6 +270,7 @@
|
||||||
- (void)openTrainStory:(id)sender;
|
- (void)openTrainStory:(id)sender;
|
||||||
- (void)loadFeedDetailView;
|
- (void)loadFeedDetailView;
|
||||||
- (void)loadTryFeedDetailView:(NSString *)feedId withStory:(NSString *)contentId isSocial:(BOOL)social withUser:(NSDictionary *)user showFindingStory:(BOOL)showHUD;
|
- (void)loadTryFeedDetailView:(NSString *)feedId withStory:(NSString *)contentId isSocial:(BOOL)social withUser:(NSDictionary *)user showFindingStory:(BOOL)showHUD;
|
||||||
|
- (void)loadStarredDetailViewWithStory:(NSString *)contentId showFindingStory:(BOOL)showHUD;
|
||||||
- (void)loadRiverFeedDetailView;
|
- (void)loadRiverFeedDetailView;
|
||||||
- (void)loadStoryDetailView;
|
- (void)loadStoryDetailView;
|
||||||
- (void)adjustStoryDetailWebView;
|
- (void)adjustStoryDetailWebView;
|
||||||
|
|
|
@ -676,7 +676,43 @@
|
||||||
[self loadFeedDetailView];
|
[self loadFeedDetailView];
|
||||||
|
|
||||||
if (showHUD) {
|
if (showHUD) {
|
||||||
[self.storyPageControl showShareHUD:@"Finding story..."];
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||||
|
[self.storyPageControl showShareHUD:@"Finding story..."];
|
||||||
|
} else {
|
||||||
|
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.feedDetailViewController.view animated:YES];
|
||||||
|
HUD.labelText = @"Finding story...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadStarredDetailViewWithStory:(NSString *)contentId
|
||||||
|
showFindingStory:(BOOL)showHUD {
|
||||||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
||||||
|
[self.navigationController popToRootViewControllerAnimated:NO];
|
||||||
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||||
|
if (self.feedsViewController.popoverController) {
|
||||||
|
[self.feedsViewController.popoverController dismissPopoverAnimated:NO];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isSocialRiverView = NO;
|
||||||
|
self.isRiverView = YES;
|
||||||
|
self.inFindingStoryMode = YES;
|
||||||
|
self.isSocialView = NO;
|
||||||
|
|
||||||
|
self.tryFeedStoryId = contentId;
|
||||||
|
self.activeFeed = nil;
|
||||||
|
self.activeFolder = @"saved_stories";
|
||||||
|
|
||||||
|
[self loadRiverFeedDetailView];
|
||||||
|
|
||||||
|
if (showHUD) {
|
||||||
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
||||||
|
[self.storyPageControl showShareHUD:@"Finding story..."];
|
||||||
|
} else {
|
||||||
|
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.feedDetailViewController.view animated:YES];
|
||||||
|
HUD.labelText = @"Finding story...";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -968,6 +1004,7 @@
|
||||||
navController.navigationItem.hidesBackButton = YES;
|
navController.navigationItem.hidesBackButton = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MBProgressHUD hideHUDForView:self.storyPageControl.view animated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)navigationController:(UINavigationController *)navController
|
- (void)navigationController:(UINavigationController *)navController
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#import "FMDatabaseAdditions.h"
|
#import "FMDatabaseAdditions.h"
|
||||||
#import "IASKAppSettingsViewController.h"
|
#import "IASKAppSettingsViewController.h"
|
||||||
#import "IASKSettingsReader.h"
|
#import "IASKSettingsReader.h"
|
||||||
|
#import "UIImageView+AFNetworking.h"
|
||||||
|
|
||||||
#define kPhoneTableViewRowHeight 31;
|
#define kPhoneTableViewRowHeight 31;
|
||||||
#define kTableViewRowHeight 31;
|
#define kTableViewRowHeight 31;
|
||||||
|
@ -550,18 +551,23 @@ static const CGFloat kFolderTitleHeight = 28;
|
||||||
// adding user avatar to left
|
// adding user avatar to left
|
||||||
NSString *url = [NSString stringWithFormat:@"%@", [[results objectForKey:@"social_profile"] objectForKey:@"photo_url"]];
|
NSString *url = [NSString stringWithFormat:@"%@", [[results objectForKey:@"social_profile"] objectForKey:@"photo_url"]];
|
||||||
NSURL * imageURL = [NSURL URLWithString:url];
|
NSURL * imageURL = [NSURL URLWithString:url];
|
||||||
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
|
|
||||||
UIImage * userAvatarImage = [UIImage imageWithData:imageData];
|
|
||||||
userAvatarImage = [Utilities roundCorneredImage:userAvatarImage radius:6];
|
|
||||||
UIButton *userAvatarButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
UIButton *userAvatarButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
|
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
|
||||||
userAvatarButton.bounds = CGRectMake(0, 0, 32, 32);
|
userAvatarButton.bounds = CGRectMake(0, 0, 32, 32);
|
||||||
userAvatarButton.frame = CGRectMake(0, 0, 32, 32);
|
userAvatarButton.frame = CGRectMake(0, 0, 32, 32);
|
||||||
[userAvatarButton addTarget:self action:@selector(showUserProfile) forControlEvents:UIControlEventTouchUpInside];
|
[userAvatarButton addTarget:self action:@selector(showUserProfile) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[userAvatarButton setImage:userAvatarImage forState:UIControlStateNormal];
|
|
||||||
UIBarButtonItem *userInfoBarButton = [[UIBarButtonItem alloc]
|
UIBarButtonItem *userInfoBarButton = [[UIBarButtonItem alloc]
|
||||||
initWithCustomView:userAvatarButton];
|
initWithCustomView:userAvatarButton];
|
||||||
|
|
||||||
|
|
||||||
|
NSMutableURLRequest *avatarRequest = [NSMutableURLRequest requestWithURL:imageURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
|
||||||
|
[avatarRequest setHTTPShouldHandleCookies:NO];
|
||||||
|
[avatarRequest setHTTPShouldUsePipelining:YES];
|
||||||
|
UIImageView *avatarImageView = [[UIImageView alloc] initWithFrame:userAvatarButton.frame];
|
||||||
|
[avatarImageView setImageWithURLRequest:avatarRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
|
||||||
|
image = [Utilities roundCorneredImage:image radius:3];
|
||||||
|
[userAvatarButton setImage:image forState:UIControlStateNormal];
|
||||||
|
} failure:nil];
|
||||||
self.navigationItem.leftBarButtonItem = userInfoBarButton;
|
self.navigationItem.leftBarButtonItem = userInfoBarButton;
|
||||||
[self setUserAvatarLayout:orientation];
|
[self setUserAvatarLayout:orientation];
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "SmallActivityCell.h"
|
#import "SmallActivityCell.h"
|
||||||
#import "NSAttributedString+Attributes.h"
|
|
||||||
#import "UIImageView+AFNetworking.h"
|
#import "UIImageView+AFNetworking.h"
|
||||||
#import <QuartzCore/QuartzCore.h>
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
|
||||||
|
@ -24,9 +23,8 @@
|
||||||
self.faviconView = favicon;
|
self.faviconView = favicon;
|
||||||
[self.contentView addSubview:favicon];
|
[self.contentView addSubview:favicon];
|
||||||
|
|
||||||
OHAttributedLabel *activity = [[OHAttributedLabel alloc] initWithFrame:CGRectZero];
|
UILabel *activity = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||||
activity.backgroundColor = [UIColor whiteColor];
|
activity.backgroundColor = [UIColor whiteColor];
|
||||||
activity.automaticallyAddLinksForType = NO;
|
|
||||||
self.activityLabel = activity;
|
self.activityLabel = activity;
|
||||||
[self.contentView addSubview:activity];
|
[self.contentView addSubview:activity];
|
||||||
|
|
||||||
|
@ -62,6 +60,7 @@
|
||||||
self.activityLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);
|
self.activityLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);
|
||||||
}
|
}
|
||||||
self.activityLabel.backgroundColor = [UIColor clearColor];
|
self.activityLabel.backgroundColor = [UIColor clearColor];
|
||||||
|
[self.activityLabel sizeToFit];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "SmallInteractionCell.h"
|
#import "SmallInteractionCell.h"
|
||||||
#import "NSAttributedString+Attributes.h"
|
|
||||||
#import "UIImageView+AFNetworking.h"
|
#import "UIImageView+AFNetworking.h"
|
||||||
#import <QuartzCore/QuartzCore.h>
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
|
||||||
|
@ -24,9 +23,8 @@
|
||||||
self.avatarView = favicon;
|
self.avatarView = favicon;
|
||||||
[self.contentView addSubview:favicon];
|
[self.contentView addSubview:favicon];
|
||||||
|
|
||||||
OHAttributedLabel *interaction = [[OHAttributedLabel alloc] initWithFrame:CGRectZero];
|
UILabel *interaction = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||||
interaction.backgroundColor = [UIColor whiteColor];
|
interaction.backgroundColor = [UIColor whiteColor];
|
||||||
interaction.automaticallyAddLinksForType = NO;
|
|
||||||
self.interactionLabel = interaction;
|
self.interactionLabel = interaction;
|
||||||
[self.contentView addSubview:interaction];
|
[self.contentView addSubview:interaction];
|
||||||
|
|
||||||
|
@ -62,6 +60,7 @@
|
||||||
self.interactionLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);
|
self.interactionLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);
|
||||||
}
|
}
|
||||||
self.interactionLabel.backgroundColor = [UIColor clearColor];
|
self.interactionLabel.backgroundColor = [UIColor clearColor];
|
||||||
|
[self.interactionLabel sizeToFit];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<key>application-identifier</key>
|
<key>application-identifier</key>
|
||||||
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
|
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||||
<key>get-task-allow</key>
|
<key>get-task-allow</key>
|
||||||
<false/>
|
<true/>
|
||||||
<key>keychain-access-groups</key>
|
<key>keychain-access-groups</key>
|
||||||
<array>
|
<array>
|
||||||
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
|
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/***********************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Olivier Halligon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
***********************************************************************************
|
|
||||||
*
|
|
||||||
* Created by Olivier Halligon (AliSoftware) on 20 Jul. 2010.
|
|
||||||
*
|
|
||||||
* Any comment or suggestion welcome. Please contact me before using this class in
|
|
||||||
* your projects. Referencing this project in your AboutBox/Credits is appreciated.
|
|
||||||
*
|
|
||||||
***********************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreText/CoreText.h>
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: NSAttributedString Additions
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@interface NSAttributedString (OHCommodityConstructors)
|
|
||||||
+(id)attributedStringWithString:(NSString*)string;
|
|
||||||
+(id)attributedStringWithAttributedString:(NSAttributedString*)attrStr;
|
|
||||||
|
|
||||||
//! Commodity method that call the following sizeConstrainedToSize:fitRange: method with NULL for the fitRange parameter
|
|
||||||
-(CGSize)sizeConstrainedToSize:(CGSize)maxSize;
|
|
||||||
//! if fitRange is not NULL, on return it will contain the used range that actually fits the constrained size.
|
|
||||||
//! Note: Use CGFLOAT_MAX for the CGSize's height if you don't want a constraint for the height.
|
|
||||||
-(CGSize)sizeConstrainedToSize:(CGSize)maxSize fitRange:(NSRange*)fitRange;
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: NSMutableAttributedString Additions
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@interface NSMutableAttributedString (OHCommodityStyleModifiers)
|
|
||||||
-(void)setFont:(UIFont*)font;
|
|
||||||
-(void)setFont:(UIFont*)font range:(NSRange)range;
|
|
||||||
-(void)setFontName:(NSString*)fontName size:(CGFloat)size;
|
|
||||||
-(void)setFontName:(NSString*)fontName size:(CGFloat)size range:(NSRange)range;
|
|
||||||
-(void)setFontFamily:(NSString*)fontFamily size:(CGFloat)size bold:(BOOL)isBold italic:(BOOL)isItalic range:(NSRange)range;
|
|
||||||
|
|
||||||
-(void)setTextColor:(UIColor*)color;
|
|
||||||
-(void)setTextColor:(UIColor*)color range:(NSRange)range;
|
|
||||||
-(void)setTextIsUnderlined:(BOOL)underlined;
|
|
||||||
-(void)setTextIsUnderlined:(BOOL)underlined range:(NSRange)range;
|
|
||||||
-(void)setTextUnderlineStyle:(int32_t)style range:(NSRange)range; //!< style is a combination of CTUnderlineStyle & CTUnderlineStyleModifiers
|
|
||||||
-(void)setTextBold:(BOOL)isBold range:(NSRange)range;
|
|
||||||
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode;
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode lineHeight:(CGFloat)lineHeight;
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode range:(NSRange)range;
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode range:(NSRange)range lineHeight:(CGFloat)lineHeight;
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
/***********************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Olivier Halligon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
***********************************************************************************
|
|
||||||
*
|
|
||||||
* Created by Olivier Halligon (AliSoftware) on 20 Jul. 2010.
|
|
||||||
*
|
|
||||||
* Any comment or suggestion welcome. Please contact me before using this class in
|
|
||||||
* your projects. Referencing this project in your AboutBox/Credits is appreciated.
|
|
||||||
*
|
|
||||||
***********************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#import "NSAttributedString+Attributes.h"
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: NS(Mutable)AttributedString Additions
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@implementation NSAttributedString (OHCommodityConstructors)
|
|
||||||
+(id)attributedStringWithString:(NSString*)string {
|
|
||||||
return string ? [[[self alloc] initWithString:string] autorelease] : nil;
|
|
||||||
}
|
|
||||||
+(id)attributedStringWithAttributedString:(NSAttributedString*)attrStr {
|
|
||||||
return attrStr ? [[[self alloc] initWithAttributedString:attrStr] autorelease] : nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(CGSize)sizeConstrainedToSize:(CGSize)maxSize {
|
|
||||||
return [self sizeConstrainedToSize:maxSize fitRange:NULL];
|
|
||||||
}
|
|
||||||
-(CGSize)sizeConstrainedToSize:(CGSize)maxSize fitRange:(NSRange*)fitRange {
|
|
||||||
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self);
|
|
||||||
CFRange fitCFRange = CFRangeMake(0,0);
|
|
||||||
CGSize sz = CTFramesetterSuggestFrameSizeWithConstraints(framesetter,CFRangeMake(0,0),NULL,maxSize,&fitCFRange);
|
|
||||||
if (framesetter) CFRelease(framesetter);
|
|
||||||
if (fitRange) *fitRange = NSMakeRange(fitCFRange.location, fitCFRange.length);
|
|
||||||
return CGSizeMake( floorf(sz.width+1) , floorf(sz.height+1) ); // take 1pt of margin for security
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@implementation NSMutableAttributedString (OHCommodityStyleModifiers)
|
|
||||||
|
|
||||||
-(void)setFont:(UIFont*)font {
|
|
||||||
[self setFontName:font.fontName size:font.pointSize];
|
|
||||||
}
|
|
||||||
-(void)setFont:(UIFont*)font range:(NSRange)range {
|
|
||||||
[self setFontName:font.fontName size:font.pointSize range:range];
|
|
||||||
}
|
|
||||||
-(void)setFontName:(NSString*)fontName size:(CGFloat)size {
|
|
||||||
[self setFontName:fontName size:size range:NSMakeRange(0,[self length])];
|
|
||||||
}
|
|
||||||
-(void)setFontName:(NSString*)fontName size:(CGFloat)size range:(NSRange)range {
|
|
||||||
// kCTFontAttributeName
|
|
||||||
CTFontRef aFont = CTFontCreateWithName((CFStringRef)fontName, size, NULL);
|
|
||||||
if (!aFont) return;
|
|
||||||
[self removeAttribute:(NSString*)kCTFontAttributeName range:range]; // Work around for Apple leak
|
|
||||||
[self addAttribute:(NSString*)kCTFontAttributeName value:(id)aFont range:range];
|
|
||||||
CFRelease(aFont);
|
|
||||||
}
|
|
||||||
-(void)setFontFamily:(NSString*)fontFamily size:(CGFloat)size bold:(BOOL)isBold italic:(BOOL)isItalic range:(NSRange)range {
|
|
||||||
// kCTFontFamilyNameAttribute + kCTFontTraitsAttribute
|
|
||||||
CTFontSymbolicTraits symTrait = (isBold?kCTFontBoldTrait:0) | (isItalic?kCTFontItalicTrait:0);
|
|
||||||
NSDictionary* trait = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:symTrait] forKey:(NSString*)kCTFontSymbolicTrait];
|
|
||||||
NSDictionary* attr = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
||||||
fontFamily,kCTFontFamilyNameAttribute,
|
|
||||||
trait,kCTFontTraitsAttribute,nil];
|
|
||||||
|
|
||||||
CTFontDescriptorRef desc = CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attr);
|
|
||||||
if (!desc) return;
|
|
||||||
CTFontRef aFont = CTFontCreateWithFontDescriptor(desc, size, NULL);
|
|
||||||
CFRelease(desc);
|
|
||||||
if (!aFont) return;
|
|
||||||
|
|
||||||
[self removeAttribute:(NSString*)kCTFontAttributeName range:range]; // Work around for Apple leak
|
|
||||||
[self addAttribute:(NSString*)kCTFontAttributeName value:(id)aFont range:range];
|
|
||||||
CFRelease(aFont);
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setTextColor:(UIColor*)color {
|
|
||||||
[self setTextColor:color range:NSMakeRange(0,[self length])];
|
|
||||||
}
|
|
||||||
-(void)setTextColor:(UIColor*)color range:(NSRange)range {
|
|
||||||
// kCTForegroundColorAttributeName
|
|
||||||
[self removeAttribute:(NSString*)kCTForegroundColorAttributeName range:range]; // Work around for Apple leak
|
|
||||||
[self addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)color.CGColor range:range];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setTextIsUnderlined:(BOOL)underlined {
|
|
||||||
[self setTextIsUnderlined:underlined range:NSMakeRange(0,[self length])];
|
|
||||||
}
|
|
||||||
-(void)setTextIsUnderlined:(BOOL)underlined range:(NSRange)range {
|
|
||||||
int32_t style = underlined ? (kCTUnderlineStyleSingle|kCTUnderlinePatternSolid) : kCTUnderlineStyleNone;
|
|
||||||
[self setTextUnderlineStyle:style range:range];
|
|
||||||
}
|
|
||||||
-(void)setTextUnderlineStyle:(int32_t)style range:(NSRange)range {
|
|
||||||
[self removeAttribute:(NSString*)kCTUnderlineStyleAttributeName range:range]; // Work around for Apple leak
|
|
||||||
[self addAttribute:(NSString*)kCTUnderlineStyleAttributeName value:[NSNumber numberWithInt:style] range:range];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setTextBold:(BOOL)isBold range:(NSRange)range {
|
|
||||||
NSUInteger startPoint = range.location;
|
|
||||||
NSRange effectiveRange;
|
|
||||||
do {
|
|
||||||
// Get font at startPoint
|
|
||||||
CTFontRef currentFont = (CTFontRef)[self attribute:(NSString*)kCTFontAttributeName atIndex:startPoint effectiveRange:&effectiveRange];
|
|
||||||
// The range for which this font is effective
|
|
||||||
NSRange fontRange = NSIntersectionRange(range, effectiveRange);
|
|
||||||
// Create bold/unbold font variant for this font and apply
|
|
||||||
CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits(currentFont, 0.0, NULL, (isBold?kCTFontBoldTrait:0), kCTFontBoldTrait);
|
|
||||||
if (newFont) {
|
|
||||||
[self removeAttribute:(NSString*)kCTFontAttributeName range:fontRange]; // Work around for Apple leak
|
|
||||||
[self addAttribute:(NSString*)kCTFontAttributeName value:(id)newFont range:fontRange];
|
|
||||||
CFRelease(newFont);
|
|
||||||
} else {
|
|
||||||
NSString* fontName = [(NSString*)CTFontCopyFullName(currentFont) autorelease];
|
|
||||||
NSLog(@"[OHAttributedLabel] Warning: can't find a bold font variant for font %@. Try another font family (like Helvetica) instead.",fontName);
|
|
||||||
}
|
|
||||||
////[self removeAttribute:(NSString*)kCTFontWeightTrait range:fontRange]; // Work around for Apple leak
|
|
||||||
////[self addAttribute:(NSString*)kCTFontWeightTrait value:(id)[NSNumber numberWithInt:1.0f] range:fontRange];
|
|
||||||
|
|
||||||
// If the fontRange was not covering the whole range, continue with next run
|
|
||||||
startPoint = NSMaxRange(effectiveRange);
|
|
||||||
} while(startPoint<NSMaxRange(range));
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode {
|
|
||||||
[self setTextAlignment:alignment lineBreakMode:lineBreakMode range:NSMakeRange(0,[self length])];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode lineHeight:(CGFloat)lineHeight{
|
|
||||||
[self setTextAlignment:alignment lineBreakMode:lineBreakMode range:NSMakeRange(0,[self length]) lineHeight:lineHeight];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode range:(NSRange)range {
|
|
||||||
// kCTParagraphStyleAttributeName > kCTParagraphStyleSpecifierAlignment
|
|
||||||
CTParagraphStyleSetting paraStyles[2] = {
|
|
||||||
{.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void*)&alignment},
|
|
||||||
{.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void*)&lineBreakMode},
|
|
||||||
};
|
|
||||||
CTParagraphStyleRef aStyle = CTParagraphStyleCreate(paraStyles, 2);
|
|
||||||
[self removeAttribute:(NSString*)kCTParagraphStyleAttributeName range:range]; // Work around for Apple leak
|
|
||||||
[self addAttribute:(NSString*)kCTParagraphStyleAttributeName value:(id)aStyle range:range];
|
|
||||||
CFRelease(aStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setTextAlignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode range:(NSRange)range lineHeight:(CGFloat)lineHeight{
|
|
||||||
|
|
||||||
CTParagraphStyleSetting paraStyles[3] = {
|
|
||||||
{.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void*)&alignment},
|
|
||||||
{.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void*)&lineBreakMode},
|
|
||||||
{.spec = kCTParagraphStyleSpecifierLineSpacing, .valueSize = sizeof(lineHeight),.value =(const void*)&lineHeight},
|
|
||||||
};
|
|
||||||
CTParagraphStyleRef aStyle = CTParagraphStyleCreate(paraStyles, 3);
|
|
||||||
[self removeAttribute:(NSString*)kCTParagraphStyleAttributeName range:range]; // Work around for Apple leak
|
|
||||||
[self addAttribute:(NSString*)kCTParagraphStyleAttributeName value:(id)aStyle range:range];
|
|
||||||
CFRelease(aStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
|
@ -163,8 +163,6 @@
|
||||||
43E8382315BC73EB000553BE /* FirstTimeUserAddSitesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 43E8381D15BC73EB000553BE /* FirstTimeUserAddSitesViewController.xib */; };
|
43E8382315BC73EB000553BE /* FirstTimeUserAddSitesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 43E8381D15BC73EB000553BE /* FirstTimeUserAddSitesViewController.xib */; };
|
||||||
43F44B1C159D8DBC00F48F8A /* FeedTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43F44B1B159D8D7200F48F8A /* FeedTableCell.m */; };
|
43F44B1C159D8DBC00F48F8A /* FeedTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43F44B1B159D8D7200F48F8A /* FeedTableCell.m */; };
|
||||||
43F6A79D15B0CDC60092EE91 /* ActivityCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43F6A79C15B0CDC60092EE91 /* ActivityCell.m */; };
|
43F6A79D15B0CDC60092EE91 /* ActivityCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43F6A79C15B0CDC60092EE91 /* ActivityCell.m */; };
|
||||||
43F6A7A615B0E1A40092EE91 /* NSAttributedString+Attributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 43F6A7A315B0E1A40092EE91 /* NSAttributedString+Attributes.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
|
||||||
43F6A7A715B0E1A40092EE91 /* OHAttributedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 43F6A7A515B0E1A40092EE91 /* OHAttributedLabel.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
|
||||||
43F6A7A915B0E1ED0092EE91 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F6A7A815B0E1ED0092EE91 /* CoreText.framework */; };
|
43F6A7A915B0E1ED0092EE91 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43F6A7A815B0E1ED0092EE91 /* CoreText.framework */; };
|
||||||
78095E34128EF30C00230C8E /* ASIAuthenticationDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 78095E25128EF30C00230C8E /* ASIAuthenticationDialog.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
78095E34128EF30C00230C8E /* ASIAuthenticationDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = 78095E25128EF30C00230C8E /* ASIAuthenticationDialog.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
||||||
78095E35128EF30D00230C8E /* ASIDownloadCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 78095E28128EF30C00230C8E /* ASIDownloadCache.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
78095E35128EF30D00230C8E /* ASIDownloadCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 78095E28128EF30C00230C8E /* ASIDownloadCache.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
|
||||||
|
@ -660,10 +658,6 @@
|
||||||
43F44B1B159D8D7200F48F8A /* FeedTableCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FeedTableCell.m; sourceTree = "<group>"; };
|
43F44B1B159D8D7200F48F8A /* FeedTableCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FeedTableCell.m; sourceTree = "<group>"; };
|
||||||
43F6A79B15B0CDC60092EE91 /* ActivityCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActivityCell.h; sourceTree = "<group>"; };
|
43F6A79B15B0CDC60092EE91 /* ActivityCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ActivityCell.h; sourceTree = "<group>"; };
|
||||||
43F6A79C15B0CDC60092EE91 /* ActivityCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ActivityCell.m; sourceTree = "<group>"; };
|
43F6A79C15B0CDC60092EE91 /* ActivityCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ActivityCell.m; sourceTree = "<group>"; };
|
||||||
43F6A7A215B0E1A40092EE91 /* NSAttributedString+Attributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+Attributes.h"; sourceTree = "<group>"; };
|
|
||||||
43F6A7A315B0E1A40092EE91 /* NSAttributedString+Attributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+Attributes.m"; sourceTree = "<group>"; };
|
|
||||||
43F6A7A415B0E1A40092EE91 /* OHAttributedLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OHAttributedLabel.h; sourceTree = "<group>"; };
|
|
||||||
43F6A7A515B0E1A40092EE91 /* OHAttributedLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OHAttributedLabel.m; sourceTree = "<group>"; };
|
|
||||||
43F6A7A815B0E1ED0092EE91 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
43F6A7A815B0E1ED0092EE91 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
||||||
78095E24128EF30C00230C8E /* ASIAuthenticationDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIAuthenticationDialog.h; sourceTree = "<group>"; };
|
78095E24128EF30C00230C8E /* ASIAuthenticationDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIAuthenticationDialog.h; sourceTree = "<group>"; };
|
||||||
78095E25128EF30C00230C8E /* ASIAuthenticationDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIAuthenticationDialog.m; sourceTree = "<group>"; };
|
78095E25128EF30C00230C8E /* ASIAuthenticationDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIAuthenticationDialog.m; sourceTree = "<group>"; };
|
||||||
|
@ -1154,10 +1148,6 @@
|
||||||
FF22FE5316E53ADC0046165A /* Underscore */,
|
FF22FE5316E53ADC0046165A /* Underscore */,
|
||||||
43A4BABE15C8663600F3B8D4 /* Popover */,
|
43A4BABE15C8663600F3B8D4 /* Popover */,
|
||||||
43A3912E15B73A7B0074B212 /* AFNetworking */,
|
43A3912E15B73A7B0074B212 /* AFNetworking */,
|
||||||
43F6A7A215B0E1A40092EE91 /* NSAttributedString+Attributes.h */,
|
|
||||||
43F6A7A315B0E1A40092EE91 /* NSAttributedString+Attributes.m */,
|
|
||||||
43F6A7A415B0E1A40092EE91 /* OHAttributedLabel.h */,
|
|
||||||
43F6A7A515B0E1A40092EE91 /* OHAttributedLabel.m */,
|
|
||||||
FFFC60821716578E00DC22E2 /* THCircularProgressView.h */,
|
FFFC60821716578E00DC22E2 /* THCircularProgressView.h */,
|
||||||
FFFC60831716578E00DC22E2 /* THCircularProgressView.m */,
|
FFFC60831716578E00DC22E2 /* THCircularProgressView.m */,
|
||||||
43A4C3E615B0099B008787B5 /* NewsBlur_Prefix.pch */,
|
43A4C3E615B0099B008787B5 /* NewsBlur_Prefix.pch */,
|
||||||
|
@ -2520,8 +2510,6 @@
|
||||||
43A4C3E415B00966008787B5 /* TransparentToolbar.m in Sources */,
|
43A4C3E415B00966008787B5 /* TransparentToolbar.m in Sources */,
|
||||||
43A4C3E515B00966008787B5 /* UIView+TKCategory.m in Sources */,
|
43A4C3E515B00966008787B5 /* UIView+TKCategory.m in Sources */,
|
||||||
43F6A79D15B0CDC60092EE91 /* ActivityCell.m in Sources */,
|
43F6A79D15B0CDC60092EE91 /* ActivityCell.m in Sources */,
|
||||||
43F6A7A615B0E1A40092EE91 /* NSAttributedString+Attributes.m in Sources */,
|
|
||||||
43F6A7A715B0E1A40092EE91 /* OHAttributedLabel.m in Sources */,
|
|
||||||
43ABBCAA15B53A1400EA3111 /* InteractionCell.m in Sources */,
|
43ABBCAA15B53A1400EA3111 /* InteractionCell.m in Sources */,
|
||||||
43A3914415B73A7B0074B212 /* AFHTTPClient.m in Sources */,
|
43A3914415B73A7B0074B212 /* AFHTTPClient.m in Sources */,
|
||||||
43A3914515B73A7B0074B212 /* AFHTTPRequestOperation.m in Sources */,
|
43A3914515B73A7B0074B212 /* AFHTTPRequestOperation.m in Sources */,
|
||||||
|
@ -2652,8 +2640,8 @@
|
||||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Distribution: NewsBlur, Inc.";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: NewsBlur, Inc.";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
@ -2676,8 +2664,8 @@
|
||||||
"-all_load",
|
"-all_load",
|
||||||
);
|
);
|
||||||
PRODUCT_NAME = NewsBlur;
|
PRODUCT_NAME = NewsBlur;
|
||||||
PROVISIONING_PROFILE = "A0156932-124B-4F8E-8B93-EE6598D778F0";
|
PROVISIONING_PROFILE = "";
|
||||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "A0156932-124B-4F8E-8B93-EE6598D778F0";
|
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALID_ARCHS = armv7;
|
VALID_ARCHS = armv7;
|
||||||
"WARNING_CFLAGS[arch=*]" = "-Wall";
|
"WARNING_CFLAGS[arch=*]" = "-Wall";
|
||||||
|
@ -2691,8 +2679,8 @@
|
||||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Distribution: NewsBlur, Inc.";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: NewsBlur, Inc.";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = YES;
|
COPY_PHASE_STRIP = YES;
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
GCC_PREFIX_HEADER = NewsBlur_Prefix.pch;
|
GCC_PREFIX_HEADER = NewsBlur_Prefix.pch;
|
||||||
|
@ -2712,8 +2700,8 @@
|
||||||
"-all_load",
|
"-all_load",
|
||||||
);
|
);
|
||||||
PRODUCT_NAME = NewsBlur;
|
PRODUCT_NAME = NewsBlur;
|
||||||
PROVISIONING_PROFILE = "A0156932-124B-4F8E-8B93-EE6598D778F0";
|
PROVISIONING_PROFILE = "";
|
||||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "A0156932-124B-4F8E-8B93-EE6598D778F0";
|
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
VALID_ARCHS = armv7;
|
VALID_ARCHS = armv7;
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
/***********************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Olivier Halligon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
***********************************************************************************
|
|
||||||
*
|
|
||||||
* Created by Olivier Halligon (AliSoftware) on 20 Jul. 2010.
|
|
||||||
*
|
|
||||||
* Any comment or suggestion welcome. Please contact me before using this class in
|
|
||||||
* your projects. Referencing this project in your AboutBox/Credits is appreciated.
|
|
||||||
*
|
|
||||||
***********************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import <CoreText/CoreText.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: Utility Functions
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
CTTextAlignment CTTextAlignmentFromUITextAlignment(UITextAlignment alignment);
|
|
||||||
CTLineBreakMode CTLineBreakModeFromUILineBreakMode(UILineBreakMode lineBreakMode);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@class OHAttributedLabel;
|
|
||||||
@protocol OHAttributedLabelDelegate <NSObject>
|
|
||||||
@optional
|
|
||||||
-(BOOL)attributedLabel:(OHAttributedLabel*)attributedLabel shouldFollowLink:(NSTextCheckingResult*)linkInfo;
|
|
||||||
-(UIColor*)colorForLink:(NSTextCheckingResult*)linkInfo underlineStyle:(int32_t*)underlineStyle; //!< Combination of CTUnderlineStyle and CTUnderlineStyleModifiers
|
|
||||||
@end
|
|
||||||
|
|
||||||
#define UITextAlignmentJustify ((UITextAlignment)kCTJustifiedTextAlignment)
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@interface OHAttributedLabel : UILabel {
|
|
||||||
NSMutableAttributedString* _attributedText; //!< Internally mutable, but externally immutable copy access only
|
|
||||||
CTFrameRef textFrame;
|
|
||||||
CGRect drawingRect;
|
|
||||||
NSMutableArray* customLinks;
|
|
||||||
NSTextCheckingResult* activeLink;
|
|
||||||
CGPoint touchStartPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attributed String accessors */
|
|
||||||
@property(nonatomic, copy) NSAttributedString* attributedText; //!< Use this instead of the "text" property inherited from UILabel to set and get text
|
|
||||||
-(void)resetAttributedText; //!< rebuild the attributedString based on UILabel's text/font/color/alignment/... properties
|
|
||||||
|
|
||||||
/* Links configuration */
|
|
||||||
@property(nonatomic, assign) NSTextCheckingTypes automaticallyAddLinksForType; //!< Defaults to NSTextCheckingTypeLink, + NSTextCheckingTypePhoneNumber if "tel:" scheme supported
|
|
||||||
@property(nonatomic, retain) UIColor* linkColor; //!< Defaults to [UIColor blueColor]. See also OHAttributedLabelDelegate
|
|
||||||
@property(nonatomic, retain) UIColor* highlightedLinkColor; //[UIColor colorWithWhite:0.2 alpha:0.5]
|
|
||||||
@property(nonatomic, assign) BOOL underlineLinks; //!< Defaults to YES. See also OHAttributedLabelDelegate
|
|
||||||
-(void)addCustomLink:(NSURL*)linkUrl inRange:(NSRange)range;
|
|
||||||
-(void)removeAllCustomLinks;
|
|
||||||
|
|
||||||
@property(nonatomic, assign) BOOL onlyCatchTouchesOnLinks; //!< If YES, pointInside will only return YES if the touch is on a link. If NO, pointInside will always return YES (Defaults to NO)
|
|
||||||
@property(nonatomic, assign) IBOutlet id<OHAttributedLabelDelegate> delegate;
|
|
||||||
|
|
||||||
@property(nonatomic, assign) BOOL centerVertically;
|
|
||||||
@property(nonatomic, assign) BOOL extendBottomToFit; //!< Allows to draw text past the bottom of the view if need. May help in rare cases (like using Emoji)
|
|
||||||
@end
|
|
|
@ -1,659 +0,0 @@
|
||||||
/***********************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010 Olivier Halligon
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
***********************************************************************************
|
|
||||||
*
|
|
||||||
* Created by Olivier Halligon (AliSoftware) on 20 Jul. 2010.
|
|
||||||
*
|
|
||||||
* Any comment or suggestion welcome. Please contact me before using this class in
|
|
||||||
* your projects. Referencing this project in your AboutBox/Credits is appreciated.
|
|
||||||
*
|
|
||||||
***********************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#import "OHAttributedLabel.h"
|
|
||||||
#import "NSAttributedString+Attributes.h"
|
|
||||||
|
|
||||||
#define OHAttributedLabel_WarnAboutKnownIssues 1
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: Private Utility methods
|
|
||||||
|
|
||||||
CGPoint CGPointFlipped(CGPoint point, CGRect bounds);
|
|
||||||
CGRect CGRectFlipped(CGRect rect, CGRect bounds);
|
|
||||||
NSRange NSRangeFromCFRange(CFRange range);
|
|
||||||
CGRect CTLineGetTypographicBoundsAsRect(CTLineRef line, CGPoint lineOrigin);
|
|
||||||
CGRect CTRunGetTypographicBoundsAsRect(CTRunRef run, CTLineRef line, CGPoint lineOrigin);
|
|
||||||
BOOL CTLineContainsCharactersFromStringRange(CTLineRef line, NSRange range);
|
|
||||||
BOOL CTRunContainsCharactersFromStringRange(CTRunRef run, NSRange range);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
CTTextAlignment CTTextAlignmentFromUITextAlignment(UITextAlignment alignment) {
|
|
||||||
switch (alignment) {
|
|
||||||
case NSTextAlignmentLeft: return kCTLeftTextAlignment;
|
|
||||||
case UITextAlignmentCenter: return kCTCenterTextAlignment;
|
|
||||||
case UITextAlignmentRight: return kCTRightTextAlignment;
|
|
||||||
case UITextAlignmentJustify: return kCTJustifiedTextAlignment; /* special OOB value if we decide to use it even if it's not really standard... */
|
|
||||||
default: return kCTNaturalTextAlignment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CTLineBreakMode CTLineBreakModeFromUILineBreakMode(UILineBreakMode lineBreakMode) {
|
|
||||||
switch (lineBreakMode) {
|
|
||||||
case UILineBreakModeWordWrap: return kCTLineBreakByWordWrapping;
|
|
||||||
case UILineBreakModeCharacterWrap: return kCTLineBreakByCharWrapping;
|
|
||||||
case UILineBreakModeClip: return kCTLineBreakByClipping;
|
|
||||||
case UILineBreakModeHeadTruncation: return kCTLineBreakByTruncatingHead;
|
|
||||||
case NSLineBreakByTruncatingTail: return kCTLineBreakByTruncatingTail;
|
|
||||||
case UILineBreakModeMiddleTruncation: return kCTLineBreakByTruncatingMiddle;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't use this method for origins. Origins always depend on the height of the rect.
|
|
||||||
CGPoint CGPointFlipped(CGPoint point, CGRect bounds) {
|
|
||||||
return CGPointMake(point.x, CGRectGetMaxY(bounds)-point.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
CGRect CGRectFlipped(CGRect rect, CGRect bounds) {
|
|
||||||
return CGRectMake(CGRectGetMinX(rect),
|
|
||||||
CGRectGetMaxY(bounds)-CGRectGetMaxY(rect),
|
|
||||||
CGRectGetWidth(rect),
|
|
||||||
CGRectGetHeight(rect));
|
|
||||||
}
|
|
||||||
|
|
||||||
NSRange NSRangeFromCFRange(CFRange range) {
|
|
||||||
return NSMakeRange(range.location, range.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Font Metrics: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/FontHandling/Tasks/GettingFontMetrics.html
|
|
||||||
CGRect CTLineGetTypographicBoundsAsRect(CTLineRef line, CGPoint lineOrigin) {
|
|
||||||
CGFloat ascent = 0;
|
|
||||||
CGFloat descent = 0;
|
|
||||||
CGFloat leading = 0;
|
|
||||||
CGFloat width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
|
|
||||||
CGFloat height = ascent + descent /* + leading */;
|
|
||||||
|
|
||||||
return CGRectMake(lineOrigin.x,
|
|
||||||
lineOrigin.y - descent,
|
|
||||||
width,
|
|
||||||
height);
|
|
||||||
}
|
|
||||||
|
|
||||||
CGRect CTRunGetTypographicBoundsAsRect(CTRunRef run, CTLineRef line, CGPoint lineOrigin) {
|
|
||||||
CGFloat ascent = 0;
|
|
||||||
CGFloat descent = 0;
|
|
||||||
CGFloat leading = 0;
|
|
||||||
CGFloat width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading);
|
|
||||||
CGFloat height = ascent + descent /* + leading */;
|
|
||||||
|
|
||||||
CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
|
|
||||||
|
|
||||||
return CGRectMake(lineOrigin.x + xOffset,
|
|
||||||
lineOrigin.y - descent,
|
|
||||||
width,
|
|
||||||
height);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL CTLineContainsCharactersFromStringRange(CTLineRef line, NSRange range) {
|
|
||||||
NSRange lineRange = NSRangeFromCFRange(CTLineGetStringRange(line));
|
|
||||||
NSRange intersectedRange = NSIntersectionRange(lineRange, range);
|
|
||||||
return (intersectedRange.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL CTRunContainsCharactersFromStringRange(CTRunRef run, NSRange range) {
|
|
||||||
NSRange runRange = NSRangeFromCFRange(CTRunGetStringRange(run));
|
|
||||||
NSRange intersectedRange = NSIntersectionRange(runRange, range);
|
|
||||||
return (intersectedRange.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: Private interface
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
@interface OHAttributedLabel(/* Private */)
|
|
||||||
-(NSTextCheckingResult*)linkAtCharacterIndex:(CFIndex)idx;
|
|
||||||
-(NSTextCheckingResult*)linkAtPoint:(CGPoint)pt;
|
|
||||||
-(NSMutableAttributedString*)attributedTextWithLinks;
|
|
||||||
-(void)resetTextFrame;
|
|
||||||
-(void)drawActiveLinkHighlightForRect:(CGRect)rect;
|
|
||||||
#if OHAttributedLabel_WarnAboutKnownIssues
|
|
||||||
-(void)warnAboutKnownIssues_CheckLineBreakMode;
|
|
||||||
-(void)warnAboutKnownIssues_CheckAdjustsFontSizeToFitWidth;
|
|
||||||
#endif
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: Implementation
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
@implementation OHAttributedLabel
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: Init/Dealloc
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
- (void)commonInit
|
|
||||||
{
|
|
||||||
customLinks = [[NSMutableArray alloc] init];
|
|
||||||
self.linkColor = [UIColor blueColor];
|
|
||||||
self.highlightedLinkColor = [UIColor colorWithWhite:0.4 alpha:0.3];
|
|
||||||
self.underlineLinks = YES;
|
|
||||||
self.automaticallyAddLinksForType = NSTextCheckingTypeLink;
|
|
||||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:0"]]) {
|
|
||||||
self.automaticallyAddLinksForType |= NSTextCheckingTypePhoneNumber;
|
|
||||||
}
|
|
||||||
self.onlyCatchTouchesOnLinks = YES;
|
|
||||||
self.userInteractionEnabled = YES;
|
|
||||||
self.contentMode = UIViewContentModeRedraw;
|
|
||||||
[self resetAttributedText];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id) initWithFrame:(CGRect)aFrame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:aFrame];
|
|
||||||
if (self != nil) {
|
|
||||||
[self commonInit];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithCoder:(NSCoder *)decoder
|
|
||||||
{
|
|
||||||
self = [super initWithCoder:decoder];
|
|
||||||
if (self != nil) {
|
|
||||||
[self commonInit];
|
|
||||||
#if OHAttributedLabel_WarnAboutKnownIssues
|
|
||||||
[self warnAboutKnownIssues_CheckLineBreakMode];
|
|
||||||
[self warnAboutKnownIssues_CheckAdjustsFontSizeToFitWidth];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)dealloc
|
|
||||||
{
|
|
||||||
[_attributedText release];
|
|
||||||
[self resetTextFrame];
|
|
||||||
|
|
||||||
[customLinks release];
|
|
||||||
self.linkColor = nil;
|
|
||||||
self.highlightedLinkColor = nil;
|
|
||||||
[activeLink release];
|
|
||||||
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: Links Mgmt
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
-(void)addCustomLink:(NSURL*)linkUrl inRange:(NSRange)range {
|
|
||||||
NSTextCheckingResult* link = [NSTextCheckingResult linkCheckingResultWithRange:range URL:linkUrl];
|
|
||||||
[customLinks addObject:link];
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
-(void)removeAllCustomLinks {
|
|
||||||
[customLinks removeAllObjects];
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSMutableAttributedString*)attributedTextWithLinks {
|
|
||||||
NSMutableAttributedString* str = [self.attributedText mutableCopy];
|
|
||||||
if (!str) return nil;
|
|
||||||
|
|
||||||
NSString* plainText = [str string];
|
|
||||||
if (plainText && (self.automaticallyAddLinksForType > 0)) {
|
|
||||||
NSError* error = nil;
|
|
||||||
NSDataDetector* linkDetector = [NSDataDetector dataDetectorWithTypes:self.automaticallyAddLinksForType error:&error];
|
|
||||||
[linkDetector enumerateMatchesInString:plainText options:0 range:NSMakeRange(0,[plainText length])
|
|
||||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
|
|
||||||
{
|
|
||||||
int32_t uStyle = self.underlineLinks ? kCTUnderlineStyleSingle : kCTUnderlineStyleNone;
|
|
||||||
UIColor* thisLinkColor = (self.delegate && [self.delegate respondsToSelector:@selector(colorForLink:underlineStyle:)])
|
|
||||||
? [self.delegate colorForLink:result underlineStyle:&uStyle] : self.linkColor;
|
|
||||||
|
|
||||||
if (thisLinkColor)
|
|
||||||
[str setTextColor:thisLinkColor range:[result range]];
|
|
||||||
if (uStyle>0)
|
|
||||||
[str setTextUnderlineStyle:uStyle range:[result range]];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
[customLinks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
|
|
||||||
{
|
|
||||||
NSTextCheckingResult* result = (NSTextCheckingResult*)obj;
|
|
||||||
|
|
||||||
int32_t uStyle = self.underlineLinks ? kCTUnderlineStyleSingle : kCTUnderlineStyleNone;
|
|
||||||
UIColor* thisLinkColor = (self.delegate && [self.delegate respondsToSelector:@selector(colorForLink:underlineStyle:)])
|
|
||||||
? [self.delegate colorForLink:result underlineStyle:&uStyle] : self.linkColor;
|
|
||||||
|
|
||||||
@try {
|
|
||||||
if (thisLinkColor)
|
|
||||||
[str setTextColor:thisLinkColor range:[result range]];
|
|
||||||
if (uStyle>0)
|
|
||||||
[str setTextUnderlineStyle:uStyle range:[result range]];
|
|
||||||
}
|
|
||||||
@catch (NSException * e) {
|
|
||||||
// Protection against NSRangeException
|
|
||||||
if ([[e name] isEqualToString:NSRangeException]) {
|
|
||||||
NSLog(@"[OHAttributedLabel] exception: %@",e);
|
|
||||||
} else {
|
|
||||||
@throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
return [str autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSTextCheckingResult*)linkAtCharacterIndex:(CFIndex)idx {
|
|
||||||
__block NSTextCheckingResult* foundResult = nil;
|
|
||||||
|
|
||||||
NSString* plainText = [_attributedText string];
|
|
||||||
if (plainText && (self.automaticallyAddLinksForType > 0)) {
|
|
||||||
NSError* error = nil;
|
|
||||||
NSDataDetector* linkDetector = [NSDataDetector dataDetectorWithTypes:self.automaticallyAddLinksForType error:&error];
|
|
||||||
[linkDetector enumerateMatchesInString:plainText options:0 range:NSMakeRange(0,[plainText length])
|
|
||||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
|
|
||||||
{
|
|
||||||
NSRange r = [result range];
|
|
||||||
if (NSLocationInRange(idx, r)) {
|
|
||||||
foundResult = [[result retain] autorelease];
|
|
||||||
*stop = YES;
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
if (foundResult) return foundResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
[customLinks enumerateObjectsUsingBlock:^(id obj, NSUInteger aidx, BOOL *stop)
|
|
||||||
{
|
|
||||||
NSRange r = [(NSTextCheckingResult*)obj range];
|
|
||||||
if (NSLocationInRange(idx, r)) {
|
|
||||||
foundResult = [[obj retain] autorelease];
|
|
||||||
*stop = YES;
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
return foundResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSTextCheckingResult*)linkAtPoint:(CGPoint)point {
|
|
||||||
static const CGFloat kVMargin = 5.f;
|
|
||||||
if (!CGRectContainsPoint(CGRectInset(drawingRect, 0, -kVMargin), point)) return nil;
|
|
||||||
|
|
||||||
CFArrayRef lines = CTFrameGetLines(textFrame);
|
|
||||||
if (!lines) return nil;
|
|
||||||
CFIndex nbLines = CFArrayGetCount(lines);
|
|
||||||
NSTextCheckingResult* link = nil;
|
|
||||||
|
|
||||||
CGPoint origins[nbLines];
|
|
||||||
CTFrameGetLineOrigins(textFrame, CFRangeMake(0,0), origins);
|
|
||||||
|
|
||||||
for (int lineIndex=0 ; lineIndex<nbLines ; ++lineIndex) {
|
|
||||||
// this actually the origin of the line rect, so we need the whole rect to flip it
|
|
||||||
CGPoint lineOriginFlipped = origins[lineIndex];
|
|
||||||
|
|
||||||
CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);
|
|
||||||
CGRect lineRectFlipped = CTLineGetTypographicBoundsAsRect(line, lineOriginFlipped);
|
|
||||||
CGRect lineRect = CGRectFlipped(lineRectFlipped, CGRectFlipped(drawingRect,self.bounds));
|
|
||||||
|
|
||||||
lineRect = CGRectInset(lineRect, 0, -kVMargin);
|
|
||||||
if (CGRectContainsPoint(lineRect, point)) {
|
|
||||||
CGPoint relativePoint = CGPointMake(point.x-CGRectGetMinX(lineRect),
|
|
||||||
point.y-CGRectGetMinY(lineRect));
|
|
||||||
CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
|
|
||||||
link = ([self linkAtCharacterIndex:idx]);
|
|
||||||
if (link) return link;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
|
|
||||||
// never return self. always return the result of [super hitTest..].
|
|
||||||
// this takes userInteraction state, enabled, alpha values etc. into account
|
|
||||||
UIView *hitResult = [super hitTest:point withEvent:event];
|
|
||||||
|
|
||||||
// don't check for links if the event was handled by one of the subviews
|
|
||||||
if (hitResult != self) {
|
|
||||||
return hitResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.onlyCatchTouchesOnLinks) {
|
|
||||||
BOOL didHitLink = ([self linkAtPoint:point] != nil);
|
|
||||||
if (!didHitLink) {
|
|
||||||
// not catch the touch if it didn't hit a link
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hitResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
|
|
||||||
UITouch* touch = [touches anyObject];
|
|
||||||
CGPoint pt = [touch locationInView:self];
|
|
||||||
|
|
||||||
[activeLink release];
|
|
||||||
activeLink = [[self linkAtPoint:pt] retain];
|
|
||||||
touchStartPoint = pt;
|
|
||||||
|
|
||||||
// we're using activeLink to draw a highlight in -drawRect:
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
|
||||||
UITouch* touch = [touches anyObject];
|
|
||||||
CGPoint pt = [touch locationInView:self];
|
|
||||||
|
|
||||||
NSTextCheckingResult *linkAtTouchesEnded = [self linkAtPoint:pt];
|
|
||||||
|
|
||||||
BOOL closeToStart = (abs(touchStartPoint.x - pt.x) < 10 && abs(touchStartPoint.y - pt.y) < 10);
|
|
||||||
|
|
||||||
// we can check on equality of the ranges themselfes since the data detectors create new results
|
|
||||||
if (activeLink && (NSEqualRanges(activeLink.range,linkAtTouchesEnded.range) || closeToStart)) {
|
|
||||||
BOOL openLink = (self.delegate && [self.delegate respondsToSelector:@selector(attributedLabel:shouldFollowLink:)])
|
|
||||||
? [self.delegate attributedLabel:self shouldFollowLink:activeLink] : YES;
|
|
||||||
if (openLink) [[UIApplication sharedApplication] openURL:activeLink.URL];
|
|
||||||
}
|
|
||||||
|
|
||||||
[activeLink release];
|
|
||||||
activeLink = nil;
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
|
||||||
[activeLink release];
|
|
||||||
activeLink = nil;
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: Drawing Text
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
-(void)resetTextFrame {
|
|
||||||
if (textFrame) {
|
|
||||||
CFRelease(textFrame);
|
|
||||||
textFrame = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawTextInRect:(CGRect)aRect
|
|
||||||
{
|
|
||||||
if (_attributedText) {
|
|
||||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
|
||||||
CGContextSaveGState(ctx);
|
|
||||||
|
|
||||||
// flipping the context to draw core text
|
|
||||||
// no need to flip our typographical bounds from now on
|
|
||||||
CGContextConcatCTM(ctx, CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f));
|
|
||||||
|
|
||||||
if (self.shadowColor) {
|
|
||||||
CGContextSetShadowWithColor(ctx, self.shadowOffset, 0.0, self.shadowColor.CGColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableAttributedString* attrStrWithLinks = [self attributedTextWithLinks];
|
|
||||||
if (self.highlighted && self.highlightedTextColor != nil) {
|
|
||||||
[attrStrWithLinks setTextColor:self.highlightedTextColor];
|
|
||||||
}
|
|
||||||
if (textFrame == NULL) {
|
|
||||||
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStrWithLinks);
|
|
||||||
drawingRect = self.bounds;
|
|
||||||
if (self.centerVertically || self.extendBottomToFit) {
|
|
||||||
CGSize sz = CTFramesetterSuggestFrameSizeWithConstraints(framesetter,CFRangeMake(0,0),NULL,CGSizeMake(drawingRect.size.width,CGFLOAT_MAX),NULL);
|
|
||||||
if (self.extendBottomToFit) {
|
|
||||||
CGFloat delta = MAX(0.f , ceilf(sz.height - drawingRect.size.height)) + 10 /* Security margin */;
|
|
||||||
drawingRect.origin.y -= delta;
|
|
||||||
drawingRect.size.height += delta;
|
|
||||||
}
|
|
||||||
if (self.centerVertically) {
|
|
||||||
drawingRect.origin.y -= (drawingRect.size.height - sz.height)/2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
|
||||||
CGPathAddRect(path, NULL, drawingRect);
|
|
||||||
textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
|
|
||||||
CGPathRelease(path);
|
|
||||||
CFRelease(framesetter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw highlights for activeLink
|
|
||||||
if (activeLink) {
|
|
||||||
[self drawActiveLinkHighlightForRect:drawingRect];
|
|
||||||
}
|
|
||||||
|
|
||||||
CTFrameDraw(textFrame, ctx);
|
|
||||||
|
|
||||||
CGContextRestoreGState(ctx);
|
|
||||||
} else {
|
|
||||||
[super drawTextInRect:aRect];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)drawActiveLinkHighlightForRect:(CGRect)rect
|
|
||||||
{
|
|
||||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
|
||||||
CGContextSaveGState(ctx);
|
|
||||||
CGContextConcatCTM(ctx, CGAffineTransformMakeTranslation(rect.origin.x, rect.origin.y));
|
|
||||||
[self.highlightedLinkColor setFill];
|
|
||||||
|
|
||||||
NSRange activeLinkRange = activeLink.range;
|
|
||||||
|
|
||||||
CFArrayRef lines = CTFrameGetLines(textFrame);
|
|
||||||
CFIndex lineCount = CFArrayGetCount(lines);
|
|
||||||
CGPoint lineOrigins[lineCount];
|
|
||||||
CTFrameGetLineOrigins(textFrame, CFRangeMake(0,0), lineOrigins);
|
|
||||||
for (CFIndex lineIndex = 0; lineIndex < lineCount; lineIndex++) {
|
|
||||||
CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);
|
|
||||||
|
|
||||||
if (!CTLineContainsCharactersFromStringRange(line, activeLinkRange)) {
|
|
||||||
continue; // with next line
|
|
||||||
}
|
|
||||||
|
|
||||||
// we use this rect to union the bounds of successive runs that belong to the same active link
|
|
||||||
CGRect unionRect = CGRectZero;
|
|
||||||
|
|
||||||
CFArrayRef runs = CTLineGetGlyphRuns(line);
|
|
||||||
CFIndex runCount = CFArrayGetCount(runs);
|
|
||||||
for (CFIndex runIndex = 0; runIndex < runCount; runIndex++) {
|
|
||||||
CTRunRef run = CFArrayGetValueAtIndex(runs, runIndex);
|
|
||||||
|
|
||||||
if (!CTRunContainsCharactersFromStringRange(run, activeLinkRange)) {
|
|
||||||
if (!CGRectIsEmpty(unionRect)) {
|
|
||||||
CGContextFillRect(ctx, unionRect);
|
|
||||||
unionRect = CGRectZero;
|
|
||||||
}
|
|
||||||
continue; // with next run
|
|
||||||
}
|
|
||||||
|
|
||||||
CGRect linkRunRect = CTRunGetTypographicBoundsAsRect(run, line, lineOrigins[lineIndex]);
|
|
||||||
linkRunRect = CGRectIntegral(linkRunRect); // putting the rect on pixel edges
|
|
||||||
linkRunRect = CGRectInset(linkRunRect, -1, -1); // increase the rect a little
|
|
||||||
if (CGRectIsEmpty(unionRect)) {
|
|
||||||
unionRect = linkRunRect;
|
|
||||||
} else {
|
|
||||||
unionRect = CGRectUnion(unionRect, linkRunRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!CGRectIsEmpty(unionRect)) {
|
|
||||||
CGContextFillRect(ctx, unionRect);
|
|
||||||
//unionRect = CGRectZero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CGContextRestoreGState(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGSize)sizeThatFits:(CGSize)size {
|
|
||||||
NSMutableAttributedString* attrStrWithLinks = [self attributedTextWithLinks];
|
|
||||||
if (!attrStrWithLinks) return CGSizeZero;
|
|
||||||
return [attrStrWithLinks sizeConstrainedToSize:size fitRange:NULL];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: Setters/Getters
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@synthesize linkColor, highlightedLinkColor, underlineLinks;
|
|
||||||
@synthesize centerVertically, automaticallyAddLinksForType, onlyCatchTouchesOnLinks, extendBottomToFit;
|
|
||||||
@synthesize delegate;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
-(void)resetAttributedText {
|
|
||||||
NSMutableAttributedString* mutAttrStr = [NSMutableAttributedString attributedStringWithString:self.text];
|
|
||||||
[mutAttrStr setFont:self.font];
|
|
||||||
[mutAttrStr setTextColor:self.textColor];
|
|
||||||
CTTextAlignment coreTextAlign = CTTextAlignmentFromUITextAlignment(self.textAlignment);
|
|
||||||
CTLineBreakMode coreTextLBMode = CTLineBreakModeFromUILineBreakMode(self.lineBreakMode);
|
|
||||||
[mutAttrStr setTextAlignment:coreTextAlign lineBreakMode:coreTextLBMode lineHeight:self.font.lineHeight];
|
|
||||||
self.attributedText = mutAttrStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSAttributedString*)attributedText {
|
|
||||||
if (!_attributedText) {
|
|
||||||
[self resetAttributedText];
|
|
||||||
}
|
|
||||||
return [[_attributedText copy] autorelease]; // immutable autoreleased copy
|
|
||||||
}
|
|
||||||
-(void)setAttributedText:(NSAttributedString*)attributedText {
|
|
||||||
[_attributedText release];
|
|
||||||
_attributedText = [attributedText mutableCopy];
|
|
||||||
[self setAccessibilityLabel:_attributedText.string];
|
|
||||||
[self removeAllCustomLinks];
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
-(void)setText:(NSString *)text {
|
|
||||||
NSString* cleanedText = [[text stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"]
|
|
||||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
||||||
[super setText:cleanedText]; // will call setNeedsDisplay too
|
|
||||||
[self resetAttributedText];
|
|
||||||
}
|
|
||||||
-(void)setFont:(UIFont *)font {
|
|
||||||
[_attributedText setFont:font];
|
|
||||||
[super setFont:font]; // will call setNeedsDisplay too
|
|
||||||
}
|
|
||||||
-(void)setTextColor:(UIColor *)color {
|
|
||||||
[_attributedText setTextColor:color];
|
|
||||||
[super setTextColor:color]; // will call setNeedsDisplay too
|
|
||||||
}
|
|
||||||
-(void)setTextAlignment:(UITextAlignment)alignment {
|
|
||||||
CTTextAlignment coreTextAlign = CTTextAlignmentFromUITextAlignment(alignment);
|
|
||||||
CTLineBreakMode coreTextLBMode = CTLineBreakModeFromUILineBreakMode(self.lineBreakMode);
|
|
||||||
[_attributedText setTextAlignment:coreTextAlign lineBreakMode:coreTextLBMode lineHeight:self.font.lineHeight];
|
|
||||||
[super setTextAlignment:alignment]; // will call setNeedsDisplay too
|
|
||||||
}
|
|
||||||
-(void)setLineBreakMode:(UILineBreakMode)lineBreakMode {
|
|
||||||
CTTextAlignment coreTextAlign = CTTextAlignmentFromUITextAlignment(self.textAlignment);
|
|
||||||
CTLineBreakMode coreTextLBMode = CTLineBreakModeFromUILineBreakMode(lineBreakMode);
|
|
||||||
[_attributedText setTextAlignment:coreTextAlign lineBreakMode:coreTextLBMode lineHeight:self.font.lineHeight];
|
|
||||||
[super setLineBreakMode:lineBreakMode]; // will call setNeedsDisplay too
|
|
||||||
|
|
||||||
#if OHAttributedLabel_WarnAboutKnownIssues
|
|
||||||
[self warnAboutKnownIssues_CheckLineBreakMode];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
-(void)setCenterVertically:(BOOL)val {
|
|
||||||
centerVertically = val;
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setAutomaticallyAddLinksForType:(NSTextCheckingTypes)types {
|
|
||||||
automaticallyAddLinksForType = types;
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setExtendBottomToFit:(BOOL)val {
|
|
||||||
extendBottomToFit = val;
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setNeedsDisplay {
|
|
||||||
[self resetTextFrame];
|
|
||||||
[super setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MARK: -
|
|
||||||
// MARK: UILabel unsupported features/known issues warnings
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#if OHAttributedLabel_WarnAboutKnownIssues
|
|
||||||
-(void)warnAboutKnownIssues_CheckLineBreakMode {
|
|
||||||
BOOL truncationMode = (self.lineBreakMode == UILineBreakModeHeadTruncation)
|
|
||||||
|| (self.lineBreakMode == UILineBreakModeMiddleTruncation)
|
|
||||||
|| (self.lineBreakMode == NSLineBreakByTruncatingTail);
|
|
||||||
if (truncationMode) {
|
|
||||||
NSLog(@"[OHAttributedLabel] Warning: \"UILineBreakMode...Truncation\" lineBreakModes not yet fully supported by CoreText and OHAttributedLabel");
|
|
||||||
NSLog(@" (truncation will appear on each paragraph instead of the whole text)");
|
|
||||||
NSLog(@" This is a known issue (Help to solve this would be greatly appreciated).");
|
|
||||||
NSLog(@" See https://github.com/AliSoftware/OHAttributedLabel/issues/3");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-(void)warnAboutKnownIssues_CheckAdjustsFontSizeToFitWidth {
|
|
||||||
if (self.adjustsFontSizeToFitWidth) {
|
|
||||||
NSLog(@"[OHAttributedLabel] Warning: \"adjustsFontSizeToFitWidth\" property not supported by CoreText and OHAttributedLabel! This property will be ignored.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-(void)setAdjustsFontSizeToFitWidth:(BOOL)value {
|
|
||||||
[super setAdjustsFontSizeToFitWidth:value];
|
|
||||||
[self warnAboutKnownIssues_CheckAdjustsFontSizeToFitWidth];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)setNumberOfLines:(NSInteger)nbLines {
|
|
||||||
NSLog(@"[OHAttributedLabel] Warning: the numberOfLines property is not yet supported by CoreText and OHAttributedLabel. (this property is ignored right now)");
|
|
||||||
NSLog(@" This is a known issue (Help to solve this would be greatly appreciated).");
|
|
||||||
NSLog(@" See https://github.com/AliSoftware/OHAttributedLabel/issues/34");
|
|
||||||
|
|
||||||
[super setNumberOfLines:nbLines];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@end
|
|
Loading…
Add table
Reference in a new issue