Question

I have a simple application that contains an in-app settings Table View Controller. When the user selects the "App Themes" cell, it segues (push) over to a UICollectionView embedded in a UIViewController.

This is 12 Cells in a 3x4 fashion where each cell has an image, a label and a checkmark image if that theme has been selected.

The cells are loaded dynamically within the class here but I'm noticing some major delay when segueing over to this UICollectionView for the first time. Every subsequent time, it works fine but on an iPhone 5s, there's some extreme noticeable lag and I'm fairly new to iOS Development and I have a feeling it's something to do with how the views get created the first time, etc, but I'm not sure.

In my viewDidLoad, I have:

- (void)viewDidLoad
{
    self.title = @"Themes";
    self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor whiteColor]};
  [super viewDidLoad];
    self.cView.dataSource = self;
    self.cView.delegate = self;


    self.themeLabels = [[NSArray alloc] initWithObjects:@"Original", @"Peacock", @"Mystical", @"Zebra", @"Simplicity", @"Rainbow", @"Prosperity", @"Leopard", @"Hypnotic", @"Dunes", @"Twirl", @"Oceanic", nil];

    self.themeImages = @[[UIImage imageNamed:@"Newiphonebackground.png"], [UIImage imageNamed:@"peacock.png"], [UIImage imageNamed:@"Purplepink.png"], [UIImage imageNamed:@"PinkZebra.png"], [UIImage imageNamed:@"Greenish.png"], [UIImage imageNamed:@"MarblePrint.png"], [UIImage imageNamed:@"Prosperity.png"], [UIImage imageNamed:@"leopard.png"], [UIImage imageNamed:@"CircleEffect.png"], [UIImage imageNamed:@"Orange3.png"], [UIImage imageNamed:@"ReddishBlack.png"], [UIImage imageNamed:@"bluey.png"]];

    [self changeAppThemes];

}

The changeAppThemes method is essentially cycling through the NSUserDefaults and seeing which theme to apply.

- (void)changeAppThemes
{
    self.selectedTheme = [[NSUserDefaults standardUserDefaults] objectForKey:@"Theme"];

    if ([self.selectedTheme isEqualToString:@"Mystical"])
    {
        self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Purplepink.png"]];
        UIImage *navBackgroundImage = [[UIImage imageNamed: @"Purplepinknav"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
        [self.navigationController.navigationBar setBackgroundImage:navBackgroundImage forBarMetrics:UIBarMetricsDefault];

        UIImage *tabBackground = [[UIImage imageNamed:@"SolidPurple.png"]
                                  resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
        [self.tabBarController.tabBar setBackgroundImage:tabBackground];
    }

etc

The viewWillAppear is:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self changeAppThemes];

    self.selectedThemeString = [[NSUserDefaults standardUserDefaults] objectForKey:@"Selection"];

    if ([self.selectedThemeString isEqualToString:@"Original"])
    {
        self.checkedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    }

    else if ([self.selectedThemeString isEqualToString:@"Oceanic"])
    {
        self.checkedIndexPath = [NSIndexPath indexPathForRow:11 inSection:0];
    }

    [self.cView reloadData];

}

The viewWillAppear before today only had the call to the changeAppThemes method without the other code but it was still very unresponsive when segueing to the UICollectionView for the first time.

The cellForItemAtIndexPath is:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{

    ThemeCell *themeCell = (ThemeCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"Theme Cell" forIndexPath:indexPath];

    NSString *cellData = [self.themeLabels objectAtIndex:indexPath.row];

    themeCell.cellLabel.text = cellData;
    themeCell.cellImages.image = self.themeImages[indexPath.row];
    UIImageView *dot = [[UIImageView alloc]initWithFrame:CGRectMake(5, 0, 1, 2)];
    dot.image=[UIImage imageNamed:@"check-white-hi.png"];

    if([self.checkedIndexPath isEqual:indexPath])
    {
        NSLog(@"Loaded");
        themeCell.backgroundView = dot;
        [themeCell addSubview:dot];

    }
    else
    {
        NSLog(@"Not Loaded");

        themeCell.backgroundView = nil;
    }

Should I be changing where I place the call to the changeAppThemes? I have removed the call from the viewWillAppear and left it in the viewDidLoad and also vice versa and in both cases, the responsiveness was really bad when opening up the UICollectionView for the first time.

Any thoughts would be really appreciated.

Was it helpful?

Solution 2

First off you don't re-use your "themeCell" (not visible at least). You should only instantiate it once and then reuse it like the way you are right now. So I suggest that you add an if statement like:

if(themeCell == nil) {
    // Do all initialization here. For example the "dot" ImageView...  
} else {
   // Reuse the "themeCell" here
}

Also the "self.themeImages" in your viewDidLoad might add some overhead, BUT it shouldn't be extreme. We are talking about 60-100ms at MAX which isn't noticeable during startup. And another big BUT: -That really depends on the size of your images! If they are huge, say 2-5MB, then that will add lots of overhead and should be done via "Lazy loading".

Also, [self changeAppThemes] are redundant in the viewDidLoad method. It shouldn't break anything if you remove it.

OTHER TIPS

Asynchronously load your images. Consider a variation of the below.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

    dispatch_async(queue, ^{
        //Do THIS
      UIImage *localImage  [self createImageObjectsforIndex:indexPath.row];
        dispatch_sync(dispatch_get_main_queue(), ^{
            //Do this when done
            cell.image = localImage;
        });

    });

For first you should reuse cells, by adding dot to the cell propotype and:

instead of this code

if([self.checkedIndexPath isEqual:indexPath])
{
    NSLog(@"Loaded");
    themeCell.backgroundView = dot;
    [themeCell addSubview:dot];

}
else
{
    NSLog(@"Not Loaded");

    themeCell.backgroundView = nil;
}

use this:

themeCell.dot.hidden = !([self.checkedIndexPath isEqual:indexPath]);

Second, resize your image file to imageView size (x2 for retina).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top