2015-07-08 14 views
10

Sto tentando di impostare il colore di sfondo/evidenziare solo il testo all'interno di uno UILabel. Il problema è che la linea si rompe e gli spazi aggiunti allo UILabel per mantenere il testo centrato sono anche in evidenza.Evidenzia solo il testo in una UILabel

enter image description here

comunicazione la spaziatura prima dell'ultima riga nella UILabel viene evidenziato. Inoltre, vengono evidenziati anche l'inizio e la fine di ogni nuova riga.

Sto creando l'esempio precedente con il seguente codice:

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

L'uscita mi piacerebbe raggiungere è quello di evidenziare solo il testo e gli spazi tra le parole, se v'è una sola spazio. La lunghezza del testo e le dimensioni dello UILabel saranno costantemente diverse per cui la codifica difficile di una soluzione non è un'opzione, sfortunatamente.

+0

http://iosdevelopertips.com/user-interface/ios-6-attributed-strings-set-text-foreground-background-strikethrough-shadow-stroke-kerning-and-more -part-1.html –

+0

@PavanJangid Non vedo come questi esempi siano diversi da quello che ho fornito. Potresti elaborare? –

+0

@DanielStorm hai provato a impostare il colore di sfondo per l'intervallo di ogni parola anziché per l'intera stringa? – Kreiri

risposta

12

Mi sembrava che l'interruzione di linea fosse il problema. La mia idea era di provare e sapere quando UILabel avrebbe aggiunto un'interruzione di riga e quindi rimuovere quel carattere dall'intervallo di caratteri in corso di evidenziazione.

Sembra che non si possa semplicemente chiedere a UILabel dove si troveranno le interruzioni di linea, ma è possibile verificare quale sarà la dimensione di una NSString quando la si aggiunge ad un'etichetta. Usando queste informazioni puoi incrementare attraverso ogni personaggio controllando costantemente l'altezza, e quando l'altezza cambia sai che hai una nuova linea.

Ho fatto un esempio che prende la stringa dell'etichetta e la separa nelle sue singole righe che appariranno in UILabel. Una volta che ho ciascuna linea, ho solo impostato il colore di sfondo su ogni riga anziché sull'intera stringa. Questo elimina e i colori di sfondo vengono impostati sulle interruzioni di riga.

Probabilmente ci sono soluzioni migliori e questo potrebbe probabilmente essere ottimizzato per prestazioni migliori, ma è un punto di partenza e sembra funzionare.

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

Hi @Sam, questo influisce sulle prestazioni? – KarenAnne

+0

Ciao @Sam, cosa succederà se il mio testo è 'Karen Neraka', e la mia linea si divide in prima riga' Karen ', la seconda linea è' Neraka'. Crollerà? Perché la seconda linea è più lunga. – KarenAnne

+0

Ciao @KarenAnne Ci sarà un piccolo anticipo per calcolare tutto prima di evidenziare l'etichetta, ma dovrebbe essere impercettibile e dipende dalla quantità di testo. Per risparmiare prestazioni, è possibile eseguire qualsiasi codice che non aggiorna l'etichetta su un thread in background. Non ci dovrebbero essere problemi o arresti anomali a causa delle dimensioni della linea. Le funzioni dovrebbero essere in grado di calcolare la dimensione corretta ed evidenziare ciò che è necessario. – Sam

1

Ho affrontato lo stesso problema e ho scoperto la soluzione più facile senza enormi costi delle prestazioni. Puoi semplicemente aggiungere TTTAttributedLabel al tuo progetto.

mio progetto dimostrativo per la domanda:

#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

1

A partire da iOS 10.3 lo stesso codice in questione ora produce il risultato desiderato. Non sono sicuro se si tratta di un bug o di una nuova funzionalità.

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

enter image description here

+0

Gli errori di solito non migliorano * le cose *. :) – NRitH

+0

@NRitH lol. L'ho menzionato come più di un avvertimento. Non considererei questa stabile come al momento. Abbiamo bisogno di più tempo o documentazione per confermare che questo codice è sicuro. –

+1

Andddd è tornato a come era prima in iOS 11.0. –

Problemi correlati