仅突出显示 UILabel 中的文本
Highlight just the text in a UILabel
我正在尝试将背景 color/highlight 设置为 UILabel
中的文本。问题是换行符和添加到 UILabel
以保持文本居中的 spaces 也被突出显示。
请注意突出显示 UILabel
中最后一行之前的间距。此外,任何新行的开头和结尾也会突出显示。
我正在使用以下代码创建上面的示例:
-(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];
// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(0, someLongStringAttr.length)];
// Set text of label
someLabel.attributedText = someLongStringAttr;
}
我想要实现的输出 是仅突出显示文本和单词之间的 spaces 如果只有一个 space .文本的长度和 UILabel
的大小将不断不同,因此不幸的是,硬编码解决方案不是一种选择。
在我看来,换行符是问题所在。
我的想法是尝试了解 UILabel 何时添加换行符,然后将该字符从突出显示的字符范围中删除。
看来您不能只询问 UILabel 换行符的位置,但您可以在将 NSString 添加到标签时检查它的大小。
使用此信息,您可以递增每个字符,不断检查高度,当高度发生变化时,您就知道换行了。
我做了一个示例,它采用 Label 的字符串并将其分成将出现在 UILabel 中的单独的行。一旦我有了每一行,我只需在每一行而不是整个字符串上设置背景颜色。这消除了在换行符处设置的背景颜色。
可能有更好的解决方案,并且可以优化这个解决方案以获得更好的性能,但它只是一个起点,而且似乎有效。
- (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;
}
我遇到过同样的问题,并找到了更简单的解决方案,而且没有巨大的性能成本。您只需将 TTTAttributedLabel 添加到您的项目即可。
我的问题演示项目:
#import "TTTAttributedLabel.h"
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *label1 = [UILabel new];
label1.textAlignment = NSTextAlignmentCenter;
label1.numberOfLines = 0;
label1.frame = CGRectMake(20, 0, CGRectGetWidth(self.view.frame) - 40, CGRectGetHeight(self.view.frame) / 2.0);
[self.view addSubview:label1];
TTTAttributedLabel *label2 = [TTTAttributedLabel new];
label2.textAlignment = NSTextAlignmentCenter;
label2.numberOfLines = 0;
label2.frame = CGRectMake(20, CGRectGetHeight(self.view.frame) / 2.0, CGRectGetWidth(self.view.frame) - 40, CGRectGetHeight(self.view.frame) / 2.0);
[self.view addSubview:label2];
NSDictionary *attributes = @{NSBackgroundColorAttributeName:[UIColor blackColor], NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont systemFontOfSize:32 weight:UIFontWeightBold]};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"Some very long string which can contain newlines and some other stuff" attributes:attributes];
label1.attributedText = string;
label2.text = string;
}
@end
从 iOS 10.3 开始,有问题的相同代码现在会产生所需的结果。不确定这是错误还是新功能。
-(void)createSomeLabel {
// Create and position my label
UILabel *someLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width - 40.0,
self.view.frame.size.height - 300.0)];
someLabel.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
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];
// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(0, someLongStringAttr.length)];
// Set text of label
someLabel.attributedText = someLongStringAttr;
}
我正在尝试将背景 color/highlight 设置为 UILabel
中的文本。问题是换行符和添加到 UILabel
以保持文本居中的 spaces 也被突出显示。
请注意突出显示 UILabel
中最后一行之前的间距。此外,任何新行的开头和结尾也会突出显示。
我正在使用以下代码创建上面的示例:
-(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];
// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(0, someLongStringAttr.length)];
// Set text of label
someLabel.attributedText = someLongStringAttr;
}
我想要实现的输出 是仅突出显示文本和单词之间的 spaces 如果只有一个 space .文本的长度和 UILabel
的大小将不断不同,因此不幸的是,硬编码解决方案不是一种选择。
在我看来,换行符是问题所在。 我的想法是尝试了解 UILabel 何时添加换行符,然后将该字符从突出显示的字符范围中删除。
看来您不能只询问 UILabel 换行符的位置,但您可以在将 NSString 添加到标签时检查它的大小。 使用此信息,您可以递增每个字符,不断检查高度,当高度发生变化时,您就知道换行了。
我做了一个示例,它采用 Label 的字符串并将其分成将出现在 UILabel 中的单独的行。一旦我有了每一行,我只需在每一行而不是整个字符串上设置背景颜色。这消除了在换行符处设置的背景颜色。
可能有更好的解决方案,并且可以优化这个解决方案以获得更好的性能,但它只是一个起点,而且似乎有效。
- (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;
}
我遇到过同样的问题,并找到了更简单的解决方案,而且没有巨大的性能成本。您只需将 TTTAttributedLabel 添加到您的项目即可。
我的问题演示项目:
#import "TTTAttributedLabel.h"
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *label1 = [UILabel new];
label1.textAlignment = NSTextAlignmentCenter;
label1.numberOfLines = 0;
label1.frame = CGRectMake(20, 0, CGRectGetWidth(self.view.frame) - 40, CGRectGetHeight(self.view.frame) / 2.0);
[self.view addSubview:label1];
TTTAttributedLabel *label2 = [TTTAttributedLabel new];
label2.textAlignment = NSTextAlignmentCenter;
label2.numberOfLines = 0;
label2.frame = CGRectMake(20, CGRectGetHeight(self.view.frame) / 2.0, CGRectGetWidth(self.view.frame) - 40, CGRectGetHeight(self.view.frame) / 2.0);
[self.view addSubview:label2];
NSDictionary *attributes = @{NSBackgroundColorAttributeName:[UIColor blackColor], NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont systemFontOfSize:32 weight:UIFontWeightBold]};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"Some very long string which can contain newlines and some other stuff" attributes:attributes];
label1.attributedText = string;
label2.text = string;
}
@end
从 iOS 10.3 开始,有问题的相同代码现在会产生所需的结果。不确定这是错误还是新功能。
-(void)createSomeLabel {
// Create and position my label
UILabel *someLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width - 40.0,
self.view.frame.size.height - 300.0)];
someLabel.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
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];
// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(0, someLongStringAttr.length)];
// Set text of label
someLabel.attributedText = someLongStringAttr;
}