2

I am creating NSMutableAttributedString in the cellForItemAtIndexPath method of my collection view. I am using NSTextAttachment to embed images in the text.

Is this a bad idea? Currently the scroll performance seems good but I am not sure if there is a better way? Would caching all the NSMutableAttributedString in a NSMutableDictionary be better for the second scroll over?

Same question can be applied to a UITableview too using cellForRowAtIndexPath.

Code:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    CellView *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[CellView reuseIdentifier] forIndexPath:indexPath];

    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    attachment.image = [UIImage imageNamed:@"eye"];
    attachment.bounds = CGRectMake(0, -2.5, 14,14);
    NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];

    NSMutableAttributedString *myString= [[NSMutableAttributedString alloc] initWithString:@""];
    [myString appendAttributedString:attachmentString];

    [myString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@" 19K    "]];

    NSTextAttachment *attachment2 = [[NSTextAttachment alloc] init];
    attachment2.image = [UIImage imageNamed:@"heart"];
    attachment2.bounds = CGRectMake(0, -2.5, 14,14);
    NSAttributedString *attachmentString2 = [NSAttributedString attributedStringWithAttachment:attachment2];

    [myString appendAttributedString:attachmentString2];

    [myString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@" 13K    "]];

    [myString enumerateAttribute:NSFontAttributeName inRange:(NSRange){0,[myString length]} options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
        [myString addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"AvenirNext-Medium" size:12] range:range];
        [myString addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithWhite:1 alpha:1] range:range];
    }];



    NSMutableAttributedString *titletext= [[NSMutableAttributedString alloc] initWithString:[self checkIfBig:indexPath]?[NSString stringWithFormat:@"\nThe Underground Railway"]:[NSString stringWithFormat:@"\nPink Oasis"]];

    [titletext enumerateAttribute:NSFontAttributeName inRange:(NSRange){0,[titletext length]} options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
        [titletext addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"AvenirNext-Medium" size:24] range:range];
        [titletext addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:range];
    }];

    [myString appendAttributedString:titletext];

    if ([self checkIfBig:indexPath]){
        NSMutableAttributedString *subtitletext= [[NSMutableAttributedString alloc] initWithString:@"\nLorem Ipsum is simply dummy text of the printing and typesetting industry."];

        [subtitletext enumerateAttribute:NSFontAttributeName inRange:(NSRange){0,[subtitletext length]} options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
            [subtitletext addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"AvenirNext-Medium" size:14] range:range];
            [subtitletext addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:range];
        }];
        [myString appendAttributedString:subtitletext];
    };

    if ([self checkIfBig:indexPath]){
        NSMutableAttributedString *subtitletext= [[NSMutableAttributedString alloc] initWithString:@"\n\n#HORROR #BLOOD"];

        [subtitletext enumerateAttribute:NSFontAttributeName inRange:(NSRange){0,[subtitletext length]} options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
            [subtitletext addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"AvenirNext-MediumItalic" size:14] range:range];
            [subtitletext addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:range];
        }];
        [myString appendAttributedString:subtitletext];
    };

    cell.titleLabel.attributedText=myString;


    return cell;
}
  • create one function in class file and just simply call it and pass data so its very easy to use why are you write this whole code in tableviewcellrowatindex method – Himanshu Moradiya Nov 29 '16 at 04:27

1 Answers1

2

As a general rule, your data should be assembled once before the table view or collection view is loaded. In your current code, as a user scrolls back and forth you are recreating the same data over and over again. That's pretty inefficient.

All of your data should be in a single array (or array of arrays if you have multiple sections). Your cellForRow|ItemAtIndexPath should simply get an object from the array and that object's properties should be used to populate the cell.

If you have many rows or some of the data is more expensive, you can improve the above by creating certain data elements on demand and caching them so you still only create each one once.

When done properly, the cellForItemAtIndexPath in your question should be less than 5 lines of code.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • awesome, thank you! So I should create all the `NSMutableAttributedString` beforehand before the `cellForRowItemAtIndexPath` gets called? – sudoExclaimationExclaimation Nov 29 '16 at 04:21
  • Start with that and see how your performance is. If all of that initial creation takes too long, there are ways to improve on that approach. – rmaddy Nov 29 '16 at 04:22