IPhone - Fullscreen UIScrollView launches to the right, but repositions itself under the navigation bar

I'm currently trying to implement a selection of photos in the same way as the Photos application, but with a custom image source. For the "photo scrolling" part, I used the Apple PhotoScroller sample code and adapted it. One of the main differences is that it is now integrated into the navigation controller (which is the photoPickerโ€™s own navigation controller, and not one of the applications), with a navigation bar. I have a status and the navigation bar is translucent, and I set wantFullScreenLayout = YES on the entire view controller used in photoPicker. It seems to be working almost fine. The browse (the one that displays the thumbnails of all the photos on the album) is really full-screen, and I have to manually shift it so that the thumbnails first appear below the navigation bar. However, there is a glitch for the scroll part. For those who do not know the photoScroller code sample, it works with a custom UIViewController (PhotoViewController) with the UIScrollView attribute (pagingScrollView) and a custom UIScrollView (ImageScrollView) set with the UIView and NSInteger index attributes. Then, ImageScrollView instances are added / removed as PhotoScroller subviews.

Below is some code:

PhotoViewController.h

@interface PhotoViewController : UIViewController <UIScrollViewDelegate> { UIScrollView *pagingScrollView; NSMutableSet *recycledPages; NSMutableSet *visiblePages; IBOutlet UIToolbar *toolbar; IBOutlet UIBarButtonItem *previousButtonItem; IBOutlet UIBarButtonItem *nextButtonItem; id<PhotoViewDataSource> dataSource; } @property(nonatomic, retain) UIScrollView *pagingScrollView; @property(nonatomic, retain) NSMutableSet *recylcledPages; @property(nonatomic, retain) NSMutableSet *visiblePages; @property(nonatomic, retain) UIToolbar *toolbar; @property(nonatomic, retain) UIBarButtonItem *previousButtonItem; @property(nonatomic, retain) UIBarButtonItem *nextButtonItem; @property(nonatomic, retain) id<PhotoViewDataSource> dataSource; 

PhotoViewController.m

 - (void)loadView { self.wantsFullScreenLayout = YES; // Configure the scrollView CGRect pagingScrollViewFrame = [self frameForPagingScrollView]; pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame]; pagingScrollView.pagingEnabled = YES; pagingScrollView.backgroundColor = [UIColor redColor]; pagingScrollView.showsVerticalScrollIndicator = NO; pagingScrollView.showsHorizontalScrollIndicator = NO; pagingScrollView.contentSize = CGSizeMake(pagingScrollViewFrame.size.width * [self.dataSource imageCount], pagingScrollViewFrame.size.height); //pagingScrollView.contentOffset = CGPointMake(0, 0); pagingScrollView.delegate = self; self.view = pagingScrollView; // TODO ? Prepare to tile content recycledPages = [[NSMutableSet alloc] init]; visiblePages = [[NSMutableSet alloc] init]; [self processPages]; } - (void)processPages { // Calculate which pages are visible CGRect visibleBounds = pagingScrollView.bounds; NSLog(@"PhotoViewController - processPages : frame = %@", NSStringFromCGRect(pagingScrollView.frame)); NSLog(@"pagingScrollView bounds = %@", NSStringFromCGRect(pagingScrollView.bounds)); NSLog(@"and contentSize = %@", NSStringFromCGSize(pagingScrollView.contentSize)); int firstNeededPageIndex = floorf(CGRectGetMinX(visibleBounds) / CGRectGetWidth(visibleBounds)); int lastNeededPageIndex = floorf((CGRectGetMaxX(visibleBounds)-1) / CGRectGetWidth(visibleBounds)); firstNeededPageIndex = MAX(firstNeededPageIndex, 0); lastNeededPageIndex = MIN(lastNeededPageIndex, [dataSource imageCount] - 1); if (lastNeededPageIndex >= 0) { // Recycle no-longer-visible pages for (ImageScrollView *page in visiblePages) { if (page.index < firstNeededPageIndex || page.index > lastNeededPageIndex) { [recycledPages addObject:page]; [page removeFromSuperview]; } } [visiblePages minusSet:recycledPages]; // add missing pages for (int index = firstNeededPageIndex; index <= lastNeededPageIndex; index++) { if (![self isDisplayingPageForIndex:index]) { ImageScrollView *page = [self dequeueRecycledPage]; if (page == nil) { page = [[[ImageScrollView alloc] init] autorelease]; } [self configurePage:page forIndex:index]; NSLog(@"PhotoViewController - processPage 2 : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds)); [pagingScrollView addSubview:page]; NSLog(@"PhotoViewController - processPage 3 : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds)); [visiblePages addObject:page]; } } } } - (ImageScrollView *)dequeueRecycledPage { ImageScrollView *page = [recycledPages anyObject]; if (page) { [[page retain] autorelease]; [recycledPages removeObject:page]; } return page; } - (BOOL)isDisplayingPageForIndex:(NSUInteger)index { BOOL foundPage = NO; for (ImageScrollView *page in visiblePages) { if (page.index == index) { foundPage = YES; break; } } return foundPage; } - (void)configurePage:(ImageScrollView *)page forIndex:(NSUInteger)index { page.index = index; page.frame = [self frameForPageAtIndex:index]; NSLog(@"PhotoViewController - configurePage : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds)); [page displayImage:[dataSource imageForImageId:index]]; NSLog(@"PhotoViewController - configurePage 2 : bounds = %@", NSStringFromCGRect(pagingScrollView.bounds)); } #pragma mark - #pragma mark ScrollView delegate methods - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [self processPages]; } #pragma mark - #pragma mark Frame calculations #define PADDING 10 - (CGRect)frameForPagingScrollView { CGRect frame = [[UIScreen mainScreen] bounds]; frame.origin.x -= PADDING; frame.size.width += (2*PADDING); return frame; } - (CGRect)frameForPageAtIndex:(NSUInteger)index { CGRect pagingScrollViewFrame = [self frameForPagingScrollView]; CGRect pageFrame = pagingScrollViewFrame; pageFrame.size.width -= (2 * PADDING); pageFrame.origin.x = (pagingScrollViewFrame.size.width * index) + PADDING; //pageFrame.origin.x = (pagingScrollViewFrame.size.width * index) - (PADDING*index*2); return pageFrame; } 

ImageScrollView.h

 @interface ImageScrollView : UIScrollView <UIScrollViewDelegate> { UIView *imageView; NSUInteger index; } @property (assign) NSUInteger index; - (void)displayImage:(UIImage *)image; //- (void)displayTiledImageNamed:(NSString *)imageName size:(CGSize)imageSize; - (void)configureForImageSize:(CGSize)imageSize; 

ImageScrollView.m

 - (void)layoutSubviews { [super layoutSubviews]; imageView.backgroundColor = [UIColor greenColor]; self.backgroundColor = [UIColor blueColor]; // center the image as it becomes smaller than the size of the screen CGSize boundsSize = self.bounds.size; CGRect frameToCenter = imageView.frame; // center horizontally if (frameToCenter.size.width < boundsSize.width) frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2; else frameToCenter.origin.x = 0; // center vertically if (frameToCenter.size.height < boundsSize.height) frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2; else frameToCenter.origin.y = 0; imageView.frame = frameToCenter; NSLog(@"imageView frame = %@", NSStringFromCGRect(frameToCenter)); if ([imageView isKindOfClass:[TilingView class]]) { // to handle the interaction between CATiledLayer and high resolution screens, we need to manually set the // tiling view contentScaleFactor to 1.0. (If we omitted this, it would be 2.0 on high resolution screens, // which would cause the CATiledLayer to ask us for tiles of the wrong scales.) imageView.contentScaleFactor = 1.0; } } #pragma mark - #pragma mark UIScrollView delegate methods - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return imageView; } #pragma mark - #pragma mark Configure scrollView to display new image (tiled or not) - (void)displayImage:(UIImage *)image { // clear the previous imageView [imageView removeFromSuperview]; [imageView release]; imageView = nil; // reset our zoomScale to 1.0 before doing any further calculations self.zoomScale = 1.0; // make a new UIImageView for the new image imageView = [[UIImageView alloc] initWithImage:image]; [self addSubview:imageView]; [self configureForImageSize:[image size]]; } - (void)configureForImageSize:(CGSize)imageSize { CGSize boundsSize = [self bounds].size; // set up our content size and min/max zoomscale CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the // maximum zoom scale to 0.5. CGFloat maxScale = /*1.0 / */[[UIScreen mainScreen] scale]; // don't let minScale exceed maxScale. (If the image is smaller than the screen, we don't want to force it to be zoomed.) if (minScale > maxScale) { minScale = maxScale; } self.contentSize = imageSize; self.maximumZoomScale = maxScale; self.minimumZoomScale = minScale; self.zoomScale = minScale; // start out with the content fully visible } 

My problem is that the pagingScrollView is shifted to the origin (0, -64) of the pixels (status bar height + navigation bar, I suppose) when I first load the instance of PhotoViewController. This leads to a ruined interface in which ImageScrollView appears under the navigation bar (start (0, 0)), and then can scroll up and down even when its height is smaller than the screen.

With some logs and breakpoints, I was able to determine that the boundaries of the pagingScrollView are good at the beginning of the loading process. They change when I scale the ImageScrollView image to fit the screen. This calls viewForZoomingInScrollView, and then calls the scrollViewDidScroll methods. PagingScrollView shifts during these calls.

I tried to set the offset manually, but when I do this in processPages, scrollView can no longer bounce up and down ...

Any help would be appreciated!

Greetings

RV

+4
source share
4 answers

Three20 has a really good implementation. If you need some fancy function, it only adapts the code for your own need.

All the hard work has already been done. Although, if you still want to implement your own version, at least look at the Three20 code and see how they do what you are trying to achieve.

How it looks: http://farm4.static.flickr.com/3432/3378117315_3bf905bdb6_o.jpg

Source Code: http://github.com/facebook/three20

-2
source

In your specific case (i.e., the PhotoViewController is contentInset the UINavigationController stack), a scroll of contentInset to PhotoViewController added to the navigation bar. All of this is better explained here and here .

That is, the PhotoViewController view is not suitable for your window borders and therefore has a place to scroll in all directions.

I found 2 possible solutions to your problem.

1 - You need to manually adjust the contentInset your pagingScrollView to compensate for the height of the navigation bar and status bar. Add this to PhotoViewController :

 - (void)viewDidLoad { [super viewDidLoad]; CGFloat topOffset = self.navigationController.navigationBar.frame.size.height + [[UIApplication sharedApplication] statusBarFrame].size.height; pagingScrollView.contentInset = UIEdgeInsetsMake(-topOffset, 0.0, 0.0, 0.0); } 

OR

2 - Since only scroll views have a contentInset , wrap the pagingScrollView in a simple UIView :

 - (void)loadView { [self setWantsFullScreenLayout:YES]; CGRect pagingScrollViewFrame = [self frameForPagingScrollView]; photoView = [[UIView alloc] initWithFrame:pagingScrollViewFrame]; pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame]; // ... configure the pagingScrollView [photoView addSubview:pagingScrollView]; self.view = photoView; // ... } 

Hope this helps!

+6
source

The main functions to be installed are as follows in your view controller. Use autosave flags in your content views. Enabling the following flags will increase the size of your view controller to full screen size. I am doing the following in viewWillAppear.

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self setWantsFullScreenLayout:YES]; // if you want to underlap status self.navigationController.navigationBar.translucent = YES; // underlap nav controller self.toolbar.translucent = YES; // if you have a toolbar. } 

more details can be found here in the apple docs:

http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html#//apple_ref/doc/uid/TP40011313-CH2

if you did something to set the frame scope in the content view, I would delete all this code, because the above should take care of it for you.

0
source

Just set the Adjust Scroll View inserts of the view controller to false from the storyboard.

0
source

All Articles