0

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.

amitsbajaj
  • 1,304
  • 1
  • 24
  • 59
  • check your images size – NeverBe Apr 01 '14 at 14:19
  • That's a very interesting point actually. I'm actually using the same images for the background of the app - so the iPhone 5 sized 640x1136 for the images of this collection cell. Now that you've mentioned image sizes, that does seem a bit excessive. Should I massively reduce the size of the images and test? – amitsbajaj Apr 01 '14 at 14:22
  • Use size of images as you designed it multiplied by 2 for retina – NeverBe Apr 01 '14 at 14:24
  • Also you need to reuse cells, you are adding dot view to the same cell many times – NeverBe Apr 01 '14 at 14:24
  • Thanks @NeverBe - I appreciate the answer. The size of the image did make a difference here but I'm just not quite sure how to reuse cells. I've now moved the UIImageView instantiation over to the the viewDidLoad which has made a difference, but I'm not quite sure how to reuse the cells – amitsbajaj Apr 02 '14 at 06:46

3 Answers3

2

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

    });
Eric
  • 4,063
  • 2
  • 27
  • 49
  • Many thanks Eric for the reply and my apologies for the late reply. I have reduced the size of the images to start with and while it has made a difference, I'm not sure if it is 100% faster yet and so I will certainly be looking into doing this asynchronously as per your suggestion. Many thanks for the answer. – amitsbajaj Apr 02 '14 at 06:44
  • I would prefer DISPATCH_QUEUE_PRIORITY_BACKGROUND here, because loading images at hight priority is really affecting fps. – kelin May 29 '15 at 07:24
  • what is createImageObjectsforIndex:indexPath?? – famfamfam Apr 01 '21 at 16:34
1

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.

Widerberg
  • 1,118
  • 1
  • 10
  • 24
  • Many thanks @Alexander W - my apologies for the late reply. I appreciate your response. With being fairly new to programming, I'm not quite sure how to use the reuse of the cell with the theme cell == nil because when I put the imageView instantiation code in there, it didn't recognise it anywhere else in the method. However, I did reduce the size of the images and I created a property for the uiimageview and loaded and instantiated it in the viewDidLoad. That seems to have made a big difference. How would I go about reusing the cell appropriately with my code just so I can make this right? – amitsbajaj Apr 02 '14 at 06:44
  • And I also removed the self changeAppThemes from the viewDidLoad which as you mentioned, didn't make a difference to the running of the code but could have contributed to the increase in speed. I'm just looking to do this the most correct way with the reusing of cells. – amitsbajaj Apr 02 '14 at 06:46
  • Do you have the following, `[self.cView registerClass:[ThemeCell class] forCellWithReuseIdentifier:@"Theme Cell"];` in either loadView or viewDidLoad? In the method `initWithFrame:(CGRect)aRect` of your implementation of ThemeCell you then add the subviews of your cells and does the initialization of for example `UIImageView *dot`. Hope this helps! – Widerberg Apr 02 '14 at 08:24
  • Many thanks Alexander - that did the trick and it looks like it's working much better. Really appreciate your assistance with this! – amitsbajaj Apr 02 '14 at 09:13
1

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).

NeverBe
  • 5,213
  • 2
  • 25
  • 39
  • Thanks @NeverBe - I couldn't get this working for some reason but I had a great deal of luck with Alexander's answer. I appreciate your time with this question and I've up voted your answer because it did point me in the right direction. Thanks again – amitsbajaj Apr 02 '14 at 09:13