现有的UITableView照片网格不能正确旋转?

我试图做一个基本的照片网格,这是一个“网格”的小“缩略图”图像,当点击去一个大版本的图像。 我正在使用UITableView来完成这一点。 我的问题是,当我尝试添加旋转支持,我不能让表格重绘与额外的一排图像网格。

这里是我的一些代码,请随时提出任何问题,因为我在我的智慧结束这一个> 🙁

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

谢谢你的帮助!

碎纸机

我认为一个表格视图是一个画廊的错误工具。 你总是会经历一些愚蠢的把多个图像放在一个单独的表格视图单元格中,你可能不会使用didSelectRowAtIndexPath逻辑等。我build议使用UIScrollView (参见下面的示例)。

但是,如果您决定使用表视图,那么[self.tableView reloadData]是正确的解决scheme。 但是你的cellForRowAtIndexPath有很多问题:

  1. 你应该只创buildUIButtons如果你正在创build你的单元格。 但是,你必须小心,但是,考虑到你的风景和人像的方向,妥善处理。 不难,但要小心。

  2. 您应该加载单元格来解耦图像的caching。 他们真的是两回事,即使可能觉得他们非常相似。 无论如何,你绝对不应该根据图像是否被caching来改变cellForRowAtIndexPath的一个单元格的基本填充; 它只是改变你的形象。

  3. 无关手头的问题,但我不会硬编码屏幕坐标。 在接下来的几个月/几年中,我将不会惊讶地发现新的iOS设备具有不同的分辨率。 就我个人而言,我可以根据我可以放置多less个缩略图cell.contentView.frame.width (可以使用cell.contentView.frame.width )来计算我可以放在屏幕上的图像cell.contentView.frame.width

  4. 也可能无关的问题在这里,我绝不会build议从viewWillAppear调用viewDidLoad 。 如果你想有一些他们都使用的一些常见的初始化例程,那么也许你可以做到这一点(但即使这是一个马虎),但鉴于该viewDidLoad调用[super viewDidLoad] ,你最终会调用super方法两次,你不知道iOS是否很酷。 可能不会是灾难性的,但我无法想象这是一个好主意。

  5. 最后,也与您的问题无关,但您不需要使用UIDeviceOrientationDidChangeNotification的通知中心,因为标准willAnimateRotationToInterfaceOrientationviewWillLayoutSubviews将为我们做这些。

如果您想从一个远程服务器延迟加载一个图库的UIScrollView版本,利用缩略图的caching,检测图像上的一个水龙头等,它可能看起来像下面这样。 我将注意力放在绝对不存在任何纵向/横向逻辑(因为它只是查看滚动视图的尺寸),但是它能够很好地处理方向的变化。 我没有包括,但是,我的代码填充NSArray *imageUrlStrings因为这显然是完全独特的一个特定的应用程序,但你可能会得到这个想法。 还有各种各样的优化你可以做(​​例如使用Reachability ),但这是一个可能为你做这项工作的画廊的shell。

 // 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 (eg 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 

我的ThumbnailCache只是一个简单的NSCache单例对象。 (不pipe你是以singleton的forms来实现,取决于你;你不需要,但是考虑到我的应用程序的特性,这很方便。)我已经更新了这个来封装二级Documentscaching(因此你正在从一个远程服务器,同时caching在NSCache对象中以获得最佳性能,如果要保存程序的后续调用,以便从服务器重新检索映像,则在“ Documents文件夹中caching二级caching,从而为您提供跨会话的持久性caching)。 这理论上可以(应该?)优化清除长时间没有使用的图像的Documents夹,但我会留给他人执行。

 // // 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 

 // // 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 

如果没有一些很好的公开的画廊课程,我会感到惊讶,但是如果你想推出自己的画廊,那么以上是可能的。