Im on an iOS app that should able to highlight text, and make it clickable too.
I read about NSAttributedString in iOS but it still more complicated than Spannable in android.
Is there any other Objective c way to do that, if not; what should i do using NSAttributedString to highlight a paragraph word by word, and how to make my text clickable.
Update:
What exactly i want that each word should be clickable and can be
highlighted as a single word in one paragraph.
I found a perfect solution using UITextView, it will make every word in within the UITextView clickable.
Firstly, Create an UITextView and add an UITapGestureRecognizer to it as follows:
CGRect textViewFrame = CGRectMake(0, 40, 100, 100);
textView = [[UITextView alloc]initWithFrame: textViewFrame];
textView.textAlignment = NSTextAlignmentCenter;
textView.backgroundColor = [UIColor clearColor];
textView.editable = NO;
textView.selectable = NO;
[self.view addSubView:textView];
// i used to `NSMutableAttributedString` highlight the text
string = [[NSMutableAttributedString alloc]initWithString:#"Any text to detect A B $ & - +"];
[string addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:40.0]
range:NSMakeRange(0, [string length])];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init] ;
[paragraphStyle setAlignment:NSTextAlignmentCenter];
[string addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [string length])];
[textView setAttributedText:string];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapRecognized:)];
//modify this number to recognizer number of tap
[singleTap setNumberOfTapsRequired:1];
[textView addGestureRecognizer:singleTap];
Then add the UITapGestureRecognizer #selector:
- (void)tapRecognized:(UITapGestureRecognizer *)recognizer{
if(recognizer.state == UIGestureRecognizerStateRecognized)
{
CGPoint point = [recognizer locationInView:recognizer.view];
NSString * detectedText = [self getWordAtPosition:point inTextView: textView];
if (![detectedText isEqualToString:#""]) {
NSLog(#"detectedText == %#", detectedText);
} }
}
All this magic is related to this method, witch can detect any touch on the UITextView and get the tapped word:
-(NSString*)getWordAtPosition:(CGPoint)pos inTextView:(UITextView*)_tv
{
//eliminate scroll offset
pos.y += _tv.contentOffset.y;
//get location in text from textposition at point
UITextPosition *tapPos = [_tv closestPositionToPoint:pos];
//fetch the word at this position (or nil, if not available)
UITextRange * wr = [_tv.tokenizer rangeEnclosingPosition:tapPos withGranularity:UITextGranularityWord inDirection:UITextLayoutDirectionRight];
return [_tv textInRange:wr];
}
And for highlighting the text:
-(void)setTextHighlited :(NSString *)txt{
for (NSString *word in [textView.attributedText componentsSeparatedByString:#" "]) {
if ([word hasPrefix:txt]) {
NSRange range=[self.textLabel.text rangeOfString:word];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:range];
}}
[textView setAttributedText:string];
}
And thats it, hope this helps others.
Unfortunately the behavior that you're looking for isn't as simple in objective-c as Spannable.
However, it can be accomplished fairly easily:
Create a UILabel or UITextView.
Highlight some of the words using NSMutableAttributedString and NSRange (see below).
Apply the attributed string to the attributedText property.
Create UIButtons with clear backgrounds and no label and position over the top of the clickable words.
Add a target to each button and create a selector method.
Here's some example code for the attributed string to get you started:
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString: [NSString stringWithFormat:#"Text containing a bold word and another"];
[string addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"HelveticaNeue-Bold" size:14] range:NSMakeRange(18,4)];
[string addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"HelveticaNeue-Bold" size:14] range:NSMakeRange(32, 7)];
label.attributedText = string;
Related
Using an exclusion path with a UITextView is not properly wrapping the words for the first and last line in the example shown (lower example). It does render properly using word-wrap with CoreText (top example image).
Here is the code for the UITextView (both use the same size bezier path, and both use the same font and paragraph settings)
NSString *testText = #"Text Kit exclusion paths ..."; //text truncated for brevity
UIBezierPath *viewPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 280, 280)];
UIBezierPath *shapePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 265, 265)];
viewPath.usesEvenOddFillRule = true;
shapePath.usesEvenOddFillRule = true;
[shapePath appendPath:viewPath];
NSMutableAttributedString *title = [[NSMutableAttributedString alloc]initWithString:testText];
UIFont *font = [UIFont fontWithName:#"BradleyHandITCTT-Bold" size:14];
[title addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, title.length)];
//add color
[title addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, title.length)];
//add alignment
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setAlignment:NSTextAlignmentCenter];
[paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
[title addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, title.length)];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0.0, 370.0, 280, 280)];
textView.textContainerInset = UIEdgeInsetsMake(0,0,0,0);
textView.textContainer.exclusionPaths = #[shapePath];
[textView.textContainer setLineBreakMode:NSLineBreakByWordWrapping];
textView.contentInset = UIEdgeInsetsMake(0,0,0,0);
textView.attributedText = title;
textView.backgroundColor = [UIColor clearColor];
It is worth noting that the Text Kit is respecting the word wrapping rules except for the first and (possibly last) line where the text does not actually fit. I need this to work with a UITextView because text entry is required, or I would be done with my Core Text example. Swift answers are acceptable as well as obj-c.
How can I make UIKIt behave as it does with Core Text, or in other words properly word wrap all of the words in the example path provided?
Actually i have a segmented control with 4 segment. I need to add attributed text in each segment like "Notification (2)". Here (2) will be in different color and Notification will be in different color.
I have search some third party library , But it doesn't work for me
Thanks & Regards
There is limitation to use attributed text as we use for label or button. But you may try below method to achieve your requirements.
-(void)SetSegmentValue:(NSString *)value forSegment:(int)index RangeOfBlueColor:(NSRange)bluecolorRange{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:value];
NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle defaultParagraphStyle]mutableCopy];
[paragrahStyle setAlignment:NSTextAlignmentCenter];
[paragrahStyle setLineBreakMode:NSLineBreakByWordWrapping];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragrahStyle range:NSMakeRange(0, value.length)];
[attributedString addAttribute:NSForegroundColorAttributeName
value:[UIColor blackColor]
range:NSMakeRange(0, value.length)];
[attributedString addAttribute:NSForegroundColorAttributeName
value:[UIColor blueColor]
range:bluecolorRange];
[attributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:13.0] range:NSMakeRange(0, value.length)];
int i =0 ;
for (UIView *v in [[[segment subviews] objectAtIndex:0] subviews]) {
if ([v isKindOfClass:[UILabel class]]&& i== index) {
UILabel *label=(UILabel *)v ;
[label setAttributedText:attributedString];
i++;
}
}
}
NOTE : You have to modified some part of code as it's just suggestion to solve your problem
In my project I want to add an attributed text in UILabel placed on the xib.
It's working perfectly, but if large text appears it shows some issues.
My current implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
_demoLabel.numberOfLines = 0;
_demoLabel.lineBreakMode = NSLineBreakByWordWrapping;
_demoLabel.attributedText = [self demoNameWithFontSize:21 andColor:[UIColor redColor]];
}
- (NSMutableAttributedString *)demoNameWithFontSize:(CGFloat)fontSize andColor:(UIColor *)color
{
NSMutableAttributedString *attributedText = nil;
NSString *demoName = #"Blah blah blah";
UIFont *demoFont = [UIFont fontWithName:#"Zapfino" size:fontSize];
attributedText = [[NSMutableAttributedString alloc] initWithString:demoName];
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = NSLineBreakByWordWrapping;
[attributedText addAttribute:NSParagraphStyleAttributeName value:paragraph range:NSMakeRange(0, [demoName length])];
[attributedText addAttribute:NSFontAttributeName value:demoFont range:NSMakeRange(0, [demoName length])];
[attributedText addAttribute:NSForegroundColorAttributeName value:color range:NSMakeRange(0, [demoName length])];
return attributedText;
}
Output:
Issue:
It is not displaying the whole text, even if I applied the NSMutableParagraphStyle.
How can I solve this ?
Alternative I found:
If I change
UIFont *demoFont = [UIFont fontWithName:#"Zapfino" size:fontSize];
to
UIFont *demoFont = [UIFont systemFontOfSize:fontSize];
It'll work and gives output like:
But the issue is I need to use custom font, can't use default font. Also cannot change the font size.
I checked UILabel class reference and googled, but couldn't find a solution. Please help me.
Is there anyway to span this text into multiple lines ?
You need to resize the UILabel to fit the text.
You can calculate the size with the boundingRectWithSize:options:context: NSAttributedString class method, which takes an attributed string and calculates the size within a set rect based on all the attributes of the string.
A UITextView (kind of modal : it is shown when pressing on a button) appears for the user to collect what he wants to write in another UITextView which is fixed.
I resize the UITextView's height (the one which is fixed), and put in it the text that has been entered by the user in the other (modal) UITextView.
But, instead of doing the line break (as it did in the modal UITextView), the fixed UITextView does not wrap the text...
How can I make the fixed UITextView wrap the text ?
Here is my code :
textsToDisplay[index] = [enterText text];
NSInteger _stringTotalLength=[[enterText text] length];
NSMutableAttributedString *attSt=[[NSMutableAttributedString alloc] initWithString:[enterText text]];
[attSt addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Helvetica" size:15.0]
range:NSMakeRange(0, _stringTotalLength)];
float height3 = ceil(attSt.size.height);
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
cellSizes[index] = CGSizeMake(screenWidth-6,height3+10);
}else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
cellSizes[index] = CGSizeMake(screenHeight-25,height3+10);
}
I store the text which was entered by the user in textsToDisplay which is a table of NSString. I am using a UICollectionView whose cells contain the fixed UITextView and I set the text in the following method :
- (UICollectionViewCell*) collectionView:(UICollectionView *)collectView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSMutableAttributedString *attSt=[[NSMutableAttributedString alloc] initWithString:textsToDisplay[index]];
NSInteger _stringTotalLength=[textsToDisplay[index] length];
[attSt addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Helvetica" size:15.0]
range:NSMakeRange(0, _stringTotalLength)];
cell.textView.attributedText =attSt;
I create the uitextview for entering code like this :
float width = 500.0f;
enterText = [[UITextView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-(width)/2, self.view.frame.size.height/2-200, width, 400)];
NSMutableAttributedString *attSt=[[NSMutableAttributedString alloc] initWithString:questionCell.textView.text];
NSInteger _stringTotalLength=[questionCell.textView.text length];
[attSt addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Helvetica" size:15.0]
range:NSMakeRange(0, _stringTotalLength)];
enterText.attributedText = attSt;
enterText.tag = 92;
enterText.backgroundColor = [UIColor whiteColor];
enterText.alpha = 1;
enterText.delegate = self;
[self.view addSubview:enterText];
and here is how I create the uitextview in a cell :
textView = [[UITextView alloc]initWithFrame:CGRectMake(50, 0, widthLabel-50, 50)];
textView.userInteractionEnabled = YES;
textView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
textView.backgroundColor = [UIColor clearColor];
and I configure it like this :
cell.textView.frame = CGRectMake(50, 0, cellSizes[index].width-50, cellSizes[index].height);
I ended up using a uilabel in each uicollectionviewcell because it is way too complex to do the line break with a uitextview.
The formatting of my textViews worked fine in iOS 6, but no longer in iOS 7. I understand with Text Kit much of the under the hood stuff has changed. It's become really quite confusing, and I'm hoping someone can help straighten it out a bit by helping me with something as simple as this.
My static UITextView originally was assigned a value for it's textColor and textAlignment properties. Then I made a NSMutableAttributedString, assigned it an attributes, then assigned it to the textView's attributedText property. The alignment and color no longer take effect in iOS 7.
How can I fix this? If these properties take no effect, than why do they exist anymore? Here's the creation of the textView:
UITextView *titleView = [[UITextView alloc]initWithFrame:CGRectMake(0, 90, 1024, 150)];
titleView.textAlignment = NSTextAlignmentCenter;
titleView.textColor = [UIColor whiteColor];
NSMutableAttributedString *title = [[NSMutableAttributedString alloc]initWithString:#"Welcome"];
UIFont *font = [UIFont fontWithName:#"Avenir-Light" size:60];
[title addAttribute:NSParagraphStyleAttributeName value:font range:NSMakeRange(0, title.length)];
titleView.attributedText = title;
[self.view addSubview:titleView];
Curious, the properties are taken into account for UILabel but not for UITextView
Why don't you just add attributes for color and alignment to the attributed string similar to the way you are doing with the font?
Something like:
NSMutableAttributedString *title = [[NSMutableAttributedString alloc]initWithString:#"Welcome"];
UIFont *font = [UIFont fontWithName:#"Avenir-Light" size:60];
[title addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, title.length)];
//add color
[title addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, title.length)];
//add alignment
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setAlignment:NSTextAlignmentCenter];
[title addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, title.length)];
titleView.attributedText = title;
Edit: Assign the text first, then change the properties and this way it works.
UITextView *titleView = [[UITextView alloc]initWithFrame:CGRectMake(0, 90, 1024, 150)];
//create attributed string and change font
NSMutableAttributedString *title = [[NSMutableAttributedString alloc]initWithString:#"Welcome"];
UIFont *font = [UIFont fontWithName:#"Avenir-Light" size:60];
[title addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, title.length)];
//assign text first, then customize properties
titleView.attributedText = title;
titleView.textAlignment = NSTextAlignmentCenter;
titleView.textColor = [UIColor whiteColor];