Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8317557
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 8, 20262026-06-08T21:38:43+00:00 2026-06-08T21:38:43+00:00

I am trying to make a basic photo grid, that is a grid of

  • 0

I am trying to make a basic photo grid, that is a “grid” of small “thumbnail” images, that when clicked go to a big version of the image. I am using a UITableView to accomplish this. My problem is that when I try adding rotation support I cant get the table to redraw the grid with an extra row of images.

Here’s some of my code, PLEASE feel free to ask any question as I’m at my wits end on this one >:(

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        if(cell==nil){
        cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    if(self.interfaceOrientation==UIInterfaceOrientationPortrait || self.interfaceOrientation==UIInterfaceOrientationPortraitUpsideDown){
        NSLog(@"portrait");
        for (int i=0; i < self.numberOfColumns; i++) {
            if (self.photoIndex < [self.photosArray count] || self.shouldReload==TRUE) {
                UIButton *imageButton = [[UIButton alloc]init];

                UIActivityIndicatorView *loadingActivityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];

                [imageButton addTarget:self action:@selector(imageClicked:) forControlEvents:UIControlEventTouchDown];
                switch (self.buttonNumber) {
                    case 1:
                        self.xCord=35.0f;
                        self.buttonNumber++;
                        break;
                    case 2:
                        self.xCord=213.25f;
                        self.buttonNumber++;
                        break;
                    case 3:
                        self.xCord=391.5f;
                        self.buttonNumber++;
                        break;
                    case 4:
                        self.xCord=569.75f;
                        self.buttonNumber=1;
                        break;
                }
                imageButton.frame = CGRectMake(self.xCord, 35.0f, 163.25f, 163.25f);
                imageButton.tag = self.photoIndex;
                imageButton.enabled=FALSE;
                imageButton.imageView.contentMode = UIViewContentModeScaleAspectFit;

                if(self.buttonsArray.count < self.photosArray.count)[self.buttonsArray addObject:imageButton];

                if([self.isInternetAvailableClass isInternetAvailable]==YES)[self downloadImages:self.photoIndex];
                cell.selectionStyle=UITableViewCellSelectionStyleNone;
                [cell addSubview:imageButton];

                if(self.photoIndex < self.buttonsArray.count){
                if(![[self.buttonsArray objectAtIndex:self.photoIndex] currentImage]){

                loadingActivityIndicator.center = imageButton.center;
                loadingActivityIndicator.hidesWhenStopped=TRUE;

                }
                }
                 if(self.activityIndicatorArray.count < self.photosArray.count){

                     [self.activityIndicatorArray addObject:loadingActivityIndicator];
                     if([self.isInternetAvailableClass isInternetAvailable]==YES){

                         [loadingActivityIndicator startAnimating];   

                     }

                 }else{
                     if(self.photoIndex < self.buttonsArray.count){
                     [self.activityIndicatorArray replaceObjectAtIndex:self.photoIndex withObject:loadingActivityIndicator];

                     }
                 }


               [cell addSubview:loadingActivityIndicator];
                self.photoIndex++;
            }
        }

    return cell;
    }else{
        NSLog(@"landscape called!");
        for (int i=0; i < self.numberOfColumns; i++) {
            if (self.photoIndex < [self.photosArray count] || self.shouldReload==TRUE ){
                NSLog(@"inside landscape called!");
                UIButton *imageButton = [[UIButton alloc]init];

                UIActivityIndicatorView *loadingActivityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];

                [imageButton addTarget:self action:@selector(imageClicked:) forControlEvents:UIControlEventTouchDown];
                switch (self.buttonNumber) {
                    case 1:
                        self.xCord=37;
                        self.buttonNumber++;
                        break;
                    case 2:
                        self.xCord=230;
                        self.buttonNumber++;
                        break;
                    case 3:
                        self.xCord=423;
                        self.buttonNumber++;
                        break;
                    case 4:
                        self.xCord=616;
                        self.buttonNumber++;
                        break;
                    case 5:
                        self.xCord=809;
                        self.buttonNumber=1;
                        break;
                }
                imageButton.frame = CGRectMake(self.xCord, 35.0f, 163.25f, 163.25f);
                imageButton.tag = self.photoIndex;
                imageButton.enabled=FALSE;
                imageButton.imageView.contentMode = UIViewContentModeScaleAspectFit;

                if(self.buttonsArray.count < self.photosArray.count){

                    [self.buttonsArray addObject:imageButton];
                }

                if([self.isInternetAvailableClass isInternetAvailable]==YES){

                [self downloadImages:self.photoIndex];
                }
                cell.selectionStyle=UITableViewCellSelectionStyleNone;
                [cell addSubview:imageButton];

                if(self.photoIndex < self.buttonsArray.count){
                if(![[self.buttonsArray objectAtIndex:self.photoIndex] currentImage]){

                    loadingActivityIndicator.center = imageButton.center;
                    loadingActivityIndicator.hidesWhenStopped=TRUE;
                }
            }
                if(self.activityIndicatorArray.count < self.photosArray.count){

                    [self.activityIndicatorArray addObject:loadingActivityIndicator];
                    if([self.isInternetAvailableClass isInternetAvailable]==YES){

                        [loadingActivityIndicator startAnimating];

                    }

                }else{
                    if(self.photoIndex < self.activityIndicatorArray.count){
                    [self.activityIndicatorArray replaceObjectAtIndex:self.photoIndex withObject:loadingActivityIndicator];
                    }
                }


                [cell addSubview:loadingActivityIndicator];
                self.photoIndex++;
            }
        }

        return cell;

    }
    if (self.shouldReload==TRUE)self.shouldReload=FALSE;
}


#pragma mark - Table view delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 205.0f;
}

#pragma mark - View Lifecycle
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.photosTableView = [[UITableView alloc]init];
    self.photosTableView.delegate=self;
    self.photosTableView.dataSource=self;
    self.photosTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    self.photosTableView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:self.photosTableView];

    self.isInternetAvailableClass = [[IsInternetAvailable alloc]init];
    self.buttonNumber=1;
    self.xCord=0.0f;
    self.downloadedImageNumber=0;
    self.buttonsArray = [[NSMutableArray alloc]init];
    self.dataDictionary = [[NSMutableDictionary alloc]init];
    self.downloadedImagesArray = [[NSMutableArray alloc]init];
    self.activityIndicatorArray = [[NSMutableArray alloc]init];
    self.photosArray = [[NSMutableArray alloc]init];
    self.largePhotosArray = [[NSMutableArray alloc]init];
    self.imageOrderDictionary = [[NSMutableDictionary alloc]init];
    self.refToDownloadedImage = [[UIImage alloc]init];
    self.unformattedPhotosArray = [[NSMutableArray alloc]init];
    self.appDelegate = (StereophotoAppDelegate *)[[UIApplication sharedApplication]delegate];
    self.shouldReload=FALSE;

    if(self.interfaceOrientation==UIInterfaceOrientationPortrait || self.interfaceOrientation==UIInterfaceOrientationPortraitUpsideDown){
        self.numberOfColumns=4;
    }else{
        self.numberOfColumns=5;
    }

    self.photoIndex=0;
    self.errorImageArray = [[NSMutableArray alloc]init];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillAppear:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(deviceDidRotate) name:UIDeviceOrientationDidChangeNotification object:nil];

    [self loadImages];
    }
-(void)viewWillAppear:(BOOL)animated{
    self.appDelegate.isSlideshowRunning=NO;
    self.appDelegate.PhotosPopoverSlideshowText = @"Start Slideshow";

    if([self.isInternetAvailableClass isInternetAvailable]==YES){

        if(self.isCommingFromNoNetworkConnectivity==YES){
            [self viewDidLoad];
        }
        if(self.photosTableView.isHidden==YES)[self.photosTableView setHidden:NO];
        self.isCommingFromNoNetworkConnectivity=NO;
    }else{

        if(self.photosTableView.isHidden==NO){

            [self.photosTableView setHidden:YES];

            self.isCommingFromNoNetworkConnectivity=YES;
        }
        UIAlertView *errorAlert = [[UIAlertView alloc]initWithTitle:@"Oops!" message:@"No Internet connection was detected! Please connect to the Internet and try again!" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
        [errorAlert show];
    }
}
-(void)viewWillDisappear:(BOOL)animated{
    for(NSURLConnection *connection in self.connectionsArray){
        [connection cancel];
    }
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
    return TRUE;
}
-(void)deviceDidRotate{
    self.photosTableView.frame=CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);

    if(self.interfaceOrientation==UIInterfaceOrientationPortrait || self.interfaceOrientation==UIInterfaceOrientationPortraitUpsideDown){
        self.numberOfColumns=4;
    }else{
        self.numberOfColumns=5;
    }
    self.shouldReload=TRUE;
    [self.photosTableView reloadData];
}
-(void)viewDidAppear:(BOOL)animated{
    self.photosTableView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);

}

thanks for ANY help!

Shredder

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-08T21:38:45+00:00Added an answer on June 8, 2026 at 9:38 pm

    I think a table view is the wrong tool for a gallery. You’re always going to go through some silliness to put multiple images in a single table view cell, you presumably aren’t going to be using the didSelectRowAtIndexPath logic, etc. I’d suggest a UIScrollView instead (see example below).

    But, if you were determined to use a table view, then [self.tableView reloadData] is the right solution. But your cellForRowAtIndexPath has quite a few problems:

    1. You should only create UIButtons if you’re creating your cell. You’ll have to take care, though, given your landscape v portrait orientation, of handling that properly. Not hard, but be careful.

    2. You should decouple the caching of images with the loading of cells. They really are two different things, even though it might feel like they’re awfully similar. Regardless, you definitely shouldn’t be changing the basics populating of a cell in cellForRowAtIndexPath based upon whether the image was cached or not; it just changes where you get the image.

    3. Unrelated to the problem at hand, but I would’t hard code screen coordinates. I wouldn’t be surprised to see new iOS devices with different resolutions over the coming months/years. Personally, I calculate how many images I can fit on the screen based upon how many thumbnails I can fit self.view.frame.width (you might use cell.contentView.frame.width).

    4. Also probably unrelated to the problem here, I would never recommend calling viewDidLoad from viewWillAppear. If you want to have some common initialization routine that they both use, then maybe you can do that (but even that is a little sloppy), but given that viewDidLoad is calling [super viewDidLoad], you’ll end up calling that super method twice, and you don’t know if iOS is cool with that. Probably not disasterous, but I can’t imagine that it’s a good idea.

    5. Finally, also unrelated to your problem, but you don’t need to use the notification center for UIDeviceOrientationDidChangeNotification because the standard willAnimateRotationToInterfaceOrientation or viewWillLayoutSubviews will do that for us.

    If you wanted to do a UIScrollView version of a gallery with lazy loading of images from a remote server, leveraging a cache for the thumbnails, detecting a tap on the images, etc. it might look like the following. I draw your attention to an absolute absence of any portrait/landscape logic (because it just looks at the dimensions of the scrollview), but it handles orientation changes perfectly well. I have not included, though, my code for populating NSArray *imageUrlStrings because that’s obviously completely unique to a particular app, but you probably get the idea nonetheless. And there are all sorts of optimizations you could do (e.g. use Reachability), but this is a shell of a gallery that might do the job for you.

    //  GalleryViewController.m
    
    #import "GalleryViewController.h"
    #import "ThumbnailCache.h"
    
    #define IMAGE_WIDTH 76.0
    #define IMAGE_HEIGHT 76.0
    
    @interface MyImage : NSObject
    
    @property (nonatomic, strong) NSString *urlString;
    @property (nonatomic, strong) UIImageView *imageView;
    @property (nonatomic, strong) UIActivityIndicatorView *activityIndicator;
    @property (nonatomic, strong) UIView *view;
    @property BOOL loading;
    @property BOOL loaded;
    
    @end
    
    @implementation MyImage
    
    // I find that I generally can get away with loading images in main queue using Documents
    // cache, too, but if your images are not optimized (e.g. are large), or if you're supporting
    // older, slower devices, you might not want to use the Documents cache in the main queue if
    // you want a smooth UI. If this is the case, change kUseDocumentsCacheInMainQueue to NO and
    // then use the Documents cache only in the background thread.
    
    #define kUseDocumentsCacheInMainQueue YES
    
    - (id)init
    {
        self = [super init];
        if (self)
        {
            _view = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, IMAGE_WIDTH, IMAGE_HEIGHT)];
            _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, IMAGE_WIDTH, IMAGE_HEIGHT)];
            _imageView.contentMode = UIViewContentModeScaleAspectFill;
            _imageView.clipsToBounds = YES;
            [_view addSubview:_imageView];
            _loading = NO;
            _loaded = NO;
        }
        return self;
    }
    
    - (void)loadImage:(dispatch_queue_t)queue
    {
        if (self.loading)
            return;
    
        self.loading = YES;
    
        ThumbnailCache *cache = [ThumbnailCache sharedManager];
    
        if (self.imageView.image == nil)
        {
            UIImage *imageFromCache = [cache objectForKey:self.urlString useDocumentsCache:kUseDocumentsCacheInMainQueue];
            if (imageFromCache)
            {
                if (self.activityIndicator)
                {
                    [self.activityIndicator stopAnimating];
                    self.activityIndicator = nil;
                }
    
                self.imageView.image = imageFromCache;
                self.loading = NO;
                self.loaded = YES;
                return;
            }
    
            if (self.activityIndicator == nil)
            {
                self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
                self.activityIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
                [self.view addSubview:self.activityIndicator];
            }
            [self.activityIndicator startAnimating];
    
            dispatch_async(queue, ^{
                if (self.loading)
                {
                    UIImage *image = nil;
    
                    // only requery cache for Documents cache if we didn't do so in the main queue
    
                    if (!kUseDocumentsCacheInMainQueue)
                        image = [cache objectForKey:self.urlString useDocumentsCache:YES];
    
                    // if we haven't gotten the image yet, retrieve it from the remote server
    
                    if (!image)
                    {
                        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:self.urlString]];
    
                        if (data)
                        {
                            image = [UIImage imageWithData:data];
                            [cache setObject:image forKey:self.urlString data:data];
                        }
                    }
    
                    // now update the UI in the main queue
    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (self.loading)
                        {
                            [self.activityIndicator stopAnimating];
                            self.activityIndicator = nil;
                            self.imageView.image = image;
                            self.loading = NO;
                            self.loaded = YES;
                        }
                    });
                }
            });
        }
    }
    
    - (void)unloadImage
    {
        NSLog(@"%s %@", __FUNCTION__, self.urlString);
    
        // remove from imageview, but not cache
    
        self.imageView.image = nil;
    
        self.loaded = NO;
        self.loading = NO;
    }
    
    @end
    
    
    @interface GalleryViewController ()
    {
        NSMutableArray *_myImages;
        dispatch_queue_t _queue;
    }
    @end
    
    @implementation GalleryViewController
    
    - (void)dealloc
    {
        if (_queue)
            dispatch_release(_queue);
    
        [[NSNotificationCenter defaultCenter] removeObserver:self 
                                                        name:UIApplicationDidBecomeActiveNotification
                                                      object:nil];
    }
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
    
        // this is not strictly necessary because `NSCache` will automatically
        // evict objects in low memory situations; it will not, though
        // remove items when you try to simulate a low memory situation
        // in the simulator, but it really will empty the cache when
        // memory really runs low
        //
        // ThumbnailCache *cache = [ThumbnailCache sharedManager];
        // [cache removeAllObjects];
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        self.scrollView.delegate = self;
    
        _queue = dispatch_queue_create("com.robertmryan.imageloader", NULL);
    
        [self loadImages];
    
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapHandler:)];
        [self.scrollView addGestureRecognizer:tap];
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
    }
    
    - (void)viewWillLayoutSubviews
    {
        NSUInteger imagesPerRow = (self.view.frame.size.width / IMAGE_WIDTH);
        CGFloat imageMargin = (self.view.frame.size.width - (IMAGE_WIDTH * imagesPerRow)) / (imagesPerRow + 1.0);
    
        NSUInteger row = 0;
        NSUInteger col = -1;
    
        for (NSUInteger i = 0; i < [_myImages count]; i++)
        {
            col++;
            if (col >= imagesPerRow)
            {
                col = 0;
                row++;
            }
    
            MyImage *myImage = [_myImages objectAtIndex:i];
            CGRect frame = myImage.view.frame;
            frame.origin.x = imageMargin + (imageMargin + IMAGE_WIDTH) * col;
            frame.origin.y = imageMargin + (imageMargin + IMAGE_HEIGHT) * row;
            myImage.view.frame = frame;
        }
    
        self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, (row + 1) * (IMAGE_HEIGHT + imageMargin) + imageMargin);
    
        [self displayVisibleImages:NO];
    }
    
    - (void)viewDidUnload
    {
        [self setScrollView:nil];
        _myImages = nil;
    
        [super viewDidUnload];
    }
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
    
        [self displayVisibleImages:NO];
    }
    
    - (void)appDidBecomeActive
    {
        [self displayVisibleImages:YES];
    }
    
    - (void)displayVisibleImages:(BOOL)forceLoad
    {
        CGPoint contentOffset = self.scrollView.contentOffset;
        CGRect  contentFrame  = self.scrollView.bounds;
        contentFrame.origin = contentOffset;
    
        for (MyImage *myImage in _myImages)
        {
            if (CGRectIntersectsRect(contentFrame, myImage.view.frame))
            {
                // if the image is visible, then make sure it's loaded
    
                if (!myImage.loaded || forceLoad)
                    [myImage loadImage:_queue];
            }
            else
            {
                // if not, go ahead and unload it to conserve memory
    
                if (myImage.loaded || myImage.loading)
                    [myImage unloadImage];
            }
        }
    }
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        [self displayVisibleImages:NO];
    }
    
    - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
    {
        CGFloat version = [[[UIDevice currentDevice] systemVersion] floatValue];
    
        if (version < 5.0)
            [self viewWillLayoutSubviews];
    }
    
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        return TRUE;
    }
    
    - (void)tapHandler:(UITapGestureRecognizer *)sender
    {
        CGPoint location = [sender locationInView:self.scrollView];
    
        for (MyImage *myImage in _myImages)
        {
            if (CGRectContainsPoint(myImage.view.frame, location))
                NSLog(@"Tapped in image %1d", myImage.view.tag);
        }
    }
    
    - (void)loadImages
    {
    
        NSArray *imageUrlStrings = [self loadImageUrls];
        _myImages = [[NSMutableArray alloc] init];
    
        NSUInteger imagesPerRow = (self.scrollView.frame.size.width / IMAGE_WIDTH);
        CGFloat imageMargin = (self.scrollView.frame.size.width - (IMAGE_WIDTH * imagesPerRow)) / (imagesPerRow + 1.0);
    
        NSInteger row = 0;
        NSInteger col = -1;
    
        for (NSUInteger i = 0; i < [imageUrlStrings count]; i++)
        {
            col++;
            if (col >= imagesPerRow)
            {
                col = 0;
                row++;
            }
    
            MyImage *myImage = [[MyImage alloc] init];
            myImage.urlString = [imageUrlStrings objectAtIndex:i];
            CGRect frame = myImage.view.frame;
            frame.origin.x = imageMargin + (imageMargin + IMAGE_WIDTH) * col;
            frame.origin.y = imageMargin + (imageMargin + IMAGE_HEIGHT) * row;
            myImage.view.frame = frame;
            [self.scrollView addSubview:myImage.view];
            myImage.view.tag = i;
    
            [_myImages addObject:myImage];
        }
    
        self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, (row + 1) * (IMAGE_HEIGHT + imageMargin) + imageMargin);
    }
    
    @end
    

    My ThumbnailCache is just a simple NSCache singleton object. (Whether you implement this as a singleton, is up to you; you don’t have to but it’s convenient given the nature of my apps.) I’ve updated this to encapsulate a secondary Documents cache (thus you’re retrieving images from a remote server, cached in both the NSCache object for optimal performance, and secondarily cached in the Documents folder if you want to save subsequent invocations of the program from needing to re-retrieve the images from the server, giving you a persistent cache across sessions). This theoretically could (should?) be optimized to purge the Documents folder of images that haven’t been used for a long time, but I’ll leave that for others to implement.

    //
    //  ThumbnailCache.h
    //
    //  Created by Robert Ryan on 7/17/12.
    //  Modified 8/9/12 to support secondary Documents-based cache.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface ThumbnailCache : NSCache
    
    + (id)sharedManager;
    
    - (id)objectForKey:(id)key useDocumentsCache:(BOOL)useDocumentsCache;
    - (void)setObject:(UIImage *)image forKey:(id)key data:(NSData *)data;
    
    @end
    

    and

    //
    //  ThumbnailCache.m
    //
    //  Created by Robert Ryan on 7/17/12.
    //  Modified 8/9/12 to support secondary Documents-based cache.
    //
    
    #import "ThumbnailCache.h"
    
    @implementation ThumbnailCache
    
    - (id)init
    {
        self = [super init];
        if (self)
        {
            self.name = @"Thumbnail cache";
        }
    
        return self;
    }
    
    + (id)sharedManager 
    {
        static ThumbnailCache *sharedMyManager = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedMyManager = [[self alloc] init];
        });
        return sharedMyManager;
    }
    
    - (id)objectForKey:(id)key
    {
        return [self objectForKey:key useDocumentsCache:YES];
    }
    
    - (id)objectForKey:(id)key useDocumentsCache:(BOOL)useDocumentsCache
    {
        // This is a variation of the standard objectForKey method which takes an additional
        // parameter, useDocumentsCache, which will tell this to also check the Documents
        // cache or not. This is a subtle refinement in case you don't want your main queue
        // to use the Documents cache. Generally, if you're dealing with optimized images,
        // this is not necessary, but if you're dealing with large images, this permutation
        // can be useful.
    
        // first, just try to get the image from the NSCache
    
        id result = [super objectForKey:key];
    
        // if successful (or if we don't want to look in the Documents cache), then just return.
    
        if (result || !useDocumentsCache)
            return result;
    
        // otherwise, if we didn't find it in the NSCache and we want to look in our Documents
        // cache, then let's do so
    
        NSString *path = [self pathInDocumentCache:key];
    
        if (!result && path)
        {
            // get the data from the remote server
    
            NSData *data = [NSData dataWithContentsOfFile:path];
            if (data)
            {
                // and if we found it, save it in our NSCache
    
                UIImage *image = [UIImage imageWithData:data];
                if (image)
                    [self setObject:image forKey:key];
    
                return image;
            }
        }
    
        return result;
    }
    
    - (void)setObject:(UIImage *)image forKey:(id)key data:(NSData *)data
    {
        // This is a variation of the standard setObject:forKey which takes an additional NSData.
        // The notion is that our cache stores UIImage objects (to save the theoretical overhead,
        // if any, of loading image data into a NSData and then into a UIImage ... I wouldn't be
        // surprised if Apple did some cool optimization of UIImage objects, though I don't know),
        // so if we want to write our image file, but don't want to convert the UIImage back to
        // a NSData, then let's just take the original NSData as a parameter.
    
        // save the object in the NSCache
    
        [super setObject:image forKey:key];
    
        // now let's also save the NSData in our Documents-based cache
    
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSString *path = [self pathInDocumentCache:key];
    
        if (path && ![fileManager fileExistsAtPath:path])
        {
            // let's create the folder if we need to
    
            NSString *folder = [path stringByDeletingLastPathComponent];
            if (![fileManager fileExistsAtPath:folder])
                [fileManager createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:nil error:nil];
    
            // and let's write the NSData to that folder
    
            [data writeToFile:path atomically:YES];
        }
    }
    
    - (NSString *)pathInDocumentCache:(NSString *)key
    {
        if (![key isKindOfClass:[NSString class]])
            return nil;
    
        NSString *docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *imagePath = [docsPath stringByAppendingPathComponent:@"image_cache"];
        NSURL *url = [NSURL URLWithString:key];
    
        return [imagePath stringByAppendingPathComponent:[url relativePath]];
    }
    
    @end
    

    I’d be surprised if there weren’t some great publicly available gallery classes, too, but if you wanted to roll your own, the above is a possibility.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I'm trying to make a basic application that displays an image from the camera,
I'm trying to make a very basic image creator using PySide. But I'm having
I've been trying to make my own basic .obj (Wavefront) renderer using the OpenGL
I was hacking around trying to make a basic orm that has support for
I'm trying to make a basic client-server application for windows phone 7 (using Mango
I am trying to make a basic iphone app that shows nearby tweets. I
I'm trying to make a basic grid based dungeon crawler. When I build&run and
I'm trying to make a basic replace code that replaces the match every time.
I am trying to make a Galaga type game in android using the basic
I'm trying to make a basic Windows application that builds a string out of

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.