突出显示UILabel中的文本

时间:2015-07-08 13:16:51

标签: ios objective-c uilabel nsmutableattributedstring

我正在尝试设置背景颜色/突出显示UILabel中的文本。问题是添加到UILabel以保持文本居中的换行符和空格也会突出显示。

enter image description here

请注意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;
}

我想要实现的输出是仅突出显示单词之间的文本和空格(如果只有一个空格)。文本的长度和UILabel的大小将不断变化,因此不幸的是,难以编写解决方案。

3 个答案:

答案 0 :(得分:11)

在我看来,换行是问题所在。 我的想法是尝试知道UILabel何时会添加换行符,然后从突出显示的字符范围中删除该字符。

看来您不能只询问UILabel换行的位置,但是当您将NSString添加到标签时,可以检查NSString的大小。 使用此信息,您可以不断检查每个角色的高度,当高度发生变化时,您知道自己有了一条新线。

我做了一个示例,它使用Label的字符串并将其分成将出现在UILabel中的各个行。一旦我有每一行,我只需在每一行设置背景颜色而不是整个字符串。这消除了在换行符上设置的背景颜色。

可能有更好的解决方案,而且这个解决方案可能会针对更好的性能进行优化,但这是一个起点,它似乎有效。

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;
}

答案 1 :(得分:1)

我遇到了同样的问题,在没有巨大性能成本的情况下找到了更简单的解决方案。您只需将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

enter image description here

答案 2 :(得分:1)

从iOS 10.3开始,相同的代码现在可以产生所需的结果。不确定这是一个错误还是一个新功能。

public class CustomAdapter extends BaseAdapter {

    Context c;
    LayoutInflater inflater;
    ArrayList<ProductObjects>  productObjects;

    public CustomAdapter(Context context,ArrayList<ProductObjects>  productObjects) {
        this.c = context;
        this .productObjects = productObjects;
    }

    @Override
    public int getCount() {
        return productObjects.size();
    }

    @Override
    public Object getItem(int position) {
        return productObjects.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ProductHolder productHolder;

        if (convertView==null) {
            inflater = LayoutInflater.from(c);
            convertView = inflater.inflate(R.layout.model, parent, false);

            productHolder = new ProductHolder();
            productHolder.nameTXT = (TextView) convertView.findViewById(R.id.nameTXT);
            productHolder.price = (TextView) convertView.findViewById(R.id.price);
            productHolder.Image = (ImageView) convertView.findViewById(R.id.product_image);

            convertView.setTag(productHolder);
        }

        else{

            productHolder = (ProductHolder) convertView.getTag();
        }
        ProductObjects productObjects;
        productObjects = (ProductObjects) this.getItem(position);

        productHolder.nameTXT.setText(productObjects.getProduct_name());
        productHolder.price.setText(productObjects.getPrice());

        PicassoClient.downloadImage(c,productObjects.getProduct_image(),productHolder.Image);


        return convertView;
    }

    public static class ProductHolder{

        TextView nameTXT,price;
        ImageView Image;

    }
}

enter image description here