Highlight Just the Text in a Uilabel

Highlight just the text in a UILabel

It seemed to me that the line break was the problem.
My idea was to try and know when the UILabel would add a line break and then just remove that character from the range of characters being highlighted.

It appears that you can't just ask UILabel where the line breaks are going to be but you can check what the size of an NSString will be when you add it to a label.
Using this information you can increment through each character constantly checking the height, and when the height changes you know you have a new line.

I have made an example that takes the Label's string and separates it into its individual lines that will appear in the UILabel. Once I have each line, I just set the background color on each line instead of the whole string. This eliminates and background colors being set on line breaks.

There are probably better solutions, and this one could probably be optimized for better performance, but it's a starting point and it appears to work.

Highlight words

- (void)createSomeLabel {
// Create and position my label
UILabel *someLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width - 40,
self.view.frame.size.height - 300)];
someLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
someLabel.textAlignment = NSTextAlignmentCenter;
someLabel.textColor = [UIColor whiteColor];
someLabel.lineBreakMode = NSLineBreakByWordWrapping;
someLabel.numberOfLines = 0;
[self.view addSubview:someLabel];

// This string will be different lengths all the time
NSString *someLongString = @"Here is a really long amount of text that is going to wordwrap/line break and I don't want to highlight the spacing. I want to just highlight the words and a single space before/after the word";

// Create attributed string
NSMutableAttributedString *someLongStringAttr=[[NSMutableAttributedString alloc] initWithString:someLongString attributes:nil];

// The idea here is to figure out where the UILabel would automatically make a line break and get each line of text separately.
// Temporarily set the label to be that string so that we can guess where the UILabel naturally puts its line breaks.
[someLabel setText:someLongString];
// Get an array of each individual line as the UILabel would present it.
NSArray *allLines = getLinesForLabel(someLabel);
[someLabel setText:@""];

// Loop through each line of text and apply the background color to just the text within that range.
// This way, no whitespace / line breaks will be highlighted.
__block int startRange = 0;
[allLines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL *stop) {

// The end range should be the length of the line, minus one for the whitespace.
// If we are on the final line, there are no more line breaks so we use the whole line length.
NSUInteger endRange = (idx+1 == allLines.count) ? line.length : line.length-1;

// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(startRange, endRange)];

// Update the start range to the next line
startRange += line.length;
}];

// Set text of label
someLabel.attributedText = someLongStringAttr;
}

#pragma mark - Utility Functions

static NSArray *getLinesForLabel(UILabel *label) {

// Get the text from the label
NSString *labelText = label.text;

// Create an array to hold the lines of text
NSMutableArray *allLines = [NSMutableArray array];

while (YES) {

// Get the length of the current line of text
int length = getLengthOfTextInFrame(label, labelText) + 1;

// Add this line of text to the array
[allLines addObject:[labelText substringToIndex:length]];

// Adjust the label text
labelText = [labelText substringFromIndex:length];

// Check for the final line
if(labelText.length<length) {
[allLines addObject:labelText];
break;
}
}

return [NSArray arrayWithArray:allLines];
}

static int getLengthOfTextInFrame(UILabel *label, NSString *text) {

// Create a block for getting the bounds of the current peice of text.
CGRect (^boundingRectForLength)(int) = ^CGRect(int length) {
NSString *cutText = [text substringToIndex:length];
CGRect textRect = [cutText boundingRectWithSize:CGSizeMake(label.frame.size.width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName : label.font}
context:nil];
return textRect;
};

// Get the frame of the string for one character
int length = 1;
int lastSpace = 1;
CGRect textRect = boundingRectForLength(length);
CGFloat oneLineHeight = CGRectGetHeight(textRect);

// Keep adding one character to the string until the height changes, then you know you have a new line
while (textRect.size.height <= oneLineHeight)
{
// If the next character is white space, save the current length.
// It could be the end of the line.
// This will not work for character wrap.
if ([[text substringWithRange:NSMakeRange (length, 1)] isEqualToString:@" "]) {
lastSpace = length;
}

// Increment length and get the new bounds
textRect = boundingRectForLength(++length);
}

return lastSpace;
}

How to highlight only text in UILabel - IOS

Most of the other solutions don't consider text that spans multiple lines while still only highlighting the text, and they are all pretty hacky involving extra subviews.

An iOS 6 and later solution is to use attributed strings:

NSMutableAttributedString *s =
[[NSMutableAttributedString alloc] initWithString:yourString];

[s addAttribute:NSBackgroundColorAttributeName
value:[UIColor greenColor]
range:NSMakeRange(0, s.length)];

label.attributedText = s;

Highlighting a substring in a UILabel

I don't quite understand what you're trying to do, but here's a model for you to work from:

let s = "Eat @my shorts" as NSString
var att = NSMutableAttributedString(string: s as String)
let r = s.rangeOfString("@\\w.*?\\b", options: .RegularExpressionSearch, range: NSMakeRange(0,s.length))
if r.length > 0 {
att.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: r)
}

That gives an attributed string "Eat @my shorts" where the word "@my" is red.

enter image description here

Hope that provides a clue...

how can I highlight only background of the text on touch on a UILabel

I suggest for a workaround. What you can do is, use custom UIButton instead of label. Set the default and highlighted image of the button according to your need.(Simple text for default, and highlighted text for highlighted).

Happy Programming

Selection and highlighting text on a label

Yes, you could use the gesture with your UILabel for highlighting text by either changing the background color or text color of your UILabel.

You could also store the current state of your UILabel using NSUserDefaults , and read it back we user launch your application.

Declare an isLabelHighlighted as BOOL for UILabel state.

UITapGestureRecognizer* myLabelGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(LabelClicked:)];
[myLabelView setUserInteractionEnabled:YES];
[myLabelView addGestureRecognizer:myLabelGesture];

-(void)LabelClicked:(UIGestureRecognizer*) gestureRecognizer
{
if(isLabelHighlighted)
{
myLabelView.highlightedTextColor = [UIColor greenColor];
}
else
{
myLabelView.highlightedTextColor = [UIColor redColor];
}
}

To store state of your UILabel.

[[NSUserDefaults standardUserDefaults] setBool:isLabelHighlighted forKey:@"yourKey"];

To access it, you should use below.

isLabelHighlighted = [[NSUserDefaults standardUserDefaults] boolForKey:@"yourKey"];

Add highlight/background to only text using Swift

As far as I have tried its not possible to get what you want simply with attributed text because using:

let attributedText = NSMutableAttributedString(string: "Evangelizing Desing Thinking",
attributes: [
.font: UIFont.systemFont(ofSize: 14),
.backgroundColor: UIColor.gray
]
)

Will add extray gray background at the end of each line. My previous answer was not good neither because it only adds a gray background on each word, not on spaces, and as @Alladinian noticed, ranges can be wrong in some cases.

So here is a hack you can use to achieve what you want. It uses multiple labels but it can be easily improved by putting labels in a custom view. So, in your viewDidLoad / CustomView function add:

    // Maximum desired width for your text
let maxLabelWidth: CGFloat = 80
// Font you used
let font = UIFont.systemFont(ofSize: 14)
// Your text
let text = "Eva ngel izing Des ing a Thin king"
// Width of a space character
let spaceWidth = NSString(string: " ").size(withAttributes: [NSAttributedString.Key.font: font]).width

// Width of a row
var currentRowWidth: CGFloat = 0
// Content of a row
var currentRow = ""
// Previous label added (we keep it to add constraint betweeen labels)
var prevLabel: UILabel?

let subStrings = text.split(separator: " ")
for subString in subStrings {
let currentWord = String(subString)
let nsCurrentWord = NSString(string: currentWord)
// Width of the new word
let currentWordWidth = nsCurrentWord.size(withAttributes: [NSAttributedString.Key.font: font]).width
// Width of the row if you add a new word
let currentWidth = currentRow.count == 0 ? currentWordWidth : currentWordWidth + spaceWidth + currentRowWidth

if currentWidth <= maxLabelWidth { // The word can be added in the current row
currentRowWidth = currentWidth
currentRow += currentRow.count == 0 ? currentWord : " " + currentWord
} else { // Its not possible to add a new word in the current row, we create a label with the current row content
prevLabel = generateLabel(with: currentRow,
font: font,
prevLabel: prevLabel)
currentRowWidth = currentWordWidth
currentRow = currentWord
}
}

// Dont forget to add the last row
generateLabel(with: currentRow,
font: font,
prevLabel: prevLabel)

Then you have to create the generateLabel function:

@discardableResult func generateLabel(with text: String,
font: UIFont,
prevLabel: UILabel?) -> UILabel {
let leftPadding: CGFloat = 50 // Left padding of the label
let topPadding: CGFloat = 100 // Top padding of (first) label

let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(label)
label.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: leftPadding).isActive = true
if let prevLabel = prevLabel {
label.topAnchor.constraint(equalTo: prevLabel.bottomAnchor).isActive = true
} else {
label.topAnchor.constraint(equalTo: self.view.topAnchor, constant: topPadding).isActive = true
}
label.font = font
label.text = text
label.backgroundColor = .gray

return label
}

Previous answer:

As Yogesh suggested, you can use attributed string:

    // Init label
let label = UILabel(frame: CGRect(x: 50, y: 50, width: 90, height: 120))
self.view.addSubview(label)
label.lineBreakMode = .byTruncatingTail
label.numberOfLines = 0
label.backgroundColor = .white

// Create attributed text
let text = "Evangelizing Desing Thinking"
let attributedText = NSMutableAttributedString(string: text,
attributes: [
.font: UIFont.systemFont(ofSize: 14)
]
)

// Find ranges of each word
let subStrings = text.split(separator: " ")
let ranges = subStrings.map { (subString) -> Range<String.Index> in
guard let range = text.range(of: subString) else {
fatalError("something wrong with substring") // This case should not happen
}
return range
}

// Apply background color for each word
ranges.forEach { (range) in
let nsRange = NSRange(range, in: text)
attributedText.addAttribute(.backgroundColor, value: UIColor.gray, range: nsRange)
}

// Finally set attributed text
label.attributedText = attributedText

How to highlight UILabel formatted phone number while searching

Thanks to @Larme, I have answered my own question :)

// Highlighted search for numbers
NSString *phoneString = @"+381 60 000343";
NSString *firstComponent = @"\\+?"; // Skip a leading + char
NSMutableArray *digits = [[NSMutableArray alloc] init];
[self.searchString enumerateSubstringsInRange:NSMakeRange(0, [self.searchString length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
[digits addObject:substring];
}];
NSString *pattern = [[NSString alloc] initWithFormat:@"%@%@", firstComponent, [digits componentsJoinedByString:@"\\s?"]];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:phoneString];
NSRange selectedRange = [[string string] rangeOfString:pattern options:NSRegularExpressionSearch];

if (selectedRange.location != NSNotFound) {
[string beginEditing];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:selectedRange];
[string endEditing];
}

cell.phoneNumberLabel.attributedText = string;


Related Topics



Leave a reply



Submit