views:

2084

answers:

1

Warning: This is my first iPhone Application so there's a lot of cargo cult programming going on here.

I've googled, searched stackoverflow, read books, and changed code over and over in an attempt to fix this and I can't. I'm using the PageScrollView class found in "iPhone SDK Application Development" by Jonathan Zdziarski. It is a class that provides a view of 5 images (or more), loading 3 into memory at one time. I want 3 of these PageScrollViews on the screen at once stacked on top of each other. A top row of 5 images to scroll horizontally through, a middle row of images to scroll horizontally through, and a bottom row of images to scroll horizontally through all while the phone is held in portrait position, I'm completely switching views in landscape mode.

PageScrollView as modified by me, anything commented out was at one point uncommented in an attempt to make this work. I tried to comment out as much as I could to isolate the problem:

    #import "PageScrollView.h"

@implementation PageScrollView

-(id)initWithFrame:(CGRect)frame {
    self = [ super initWithFrame: frame ];
    if (self != nil) {
     _pages = nil;
     _zeroPage = 0;
     _pageRegion = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
     //_controlRegion = CGRectMake(frame.origin.x,frame.size.height/2, frame.size.width, 0);

     self.delegate = nil;

     scrollView = [ [ UIScrollView alloc ] initWithFrame: _pageRegion ];
     scrollView.pagingEnabled = YES;
     scrollView.delegate = self;
     [ scrollView setContentOffset: CGPointMake(frame.origin.x, frame.origin.y) ];
     [ scrollView setContentSize: CGSizeMake(_pageRegion.size.width,_pageRegion.size.height) ]; 
     [ scrollView setContentInset : UIEdgeInsetsMake(0.0,80.0,0,0) ];
     [ self addSubview: scrollView ];

     //pageControl = [ [ UIPageControl alloc ] initWithFrame: _controlRegion ];
     //[ pageControl addTarget: self action: @selector(pageControlDidChange:) forControlEvents: UIControlEventValueChanged ];
     //[ self addSubview: pageControl ];
    }
    return self;
}

-(void)setPages:(NSMutableArray *)pages {
    if (pages != nil) {
        for(int i=0;i<[_pages count];i++) {
         [ [ _pages objectAtIndex: i ] removeFromSuperview ];
        }
    }
    _pages = pages;
    //scrollView.contentOffset = CGPointMake(0.0, 0.0);
    scrollView.contentOffset = CGPointMake(0.0, _pageRegion.origin.y);
    if ([ _pages count] < 3) {
        scrollView.contentSize = CGSizeMake(_pageRegion.size.width * [ _pages count ], _pageRegion.size.height);
    } else {
     scrollView.contentSize = CGSizeMake(_pageRegion.size.width * 3, _pageRegion.size.height);
     scrollView.showsHorizontalScrollIndicator = NO;
    }
    pageControl.numberOfPages = [ _pages count ];
    pageControl.currentPage = 0;
    [ self layoutViews ];
}

- (void)layoutViews {
    if ([ _pages count ] <= 3) {
     for(int i=0;i<[ _pages count];i++) {
      UIView *page = [ _pages objectAtIndex: i ];
      //CGRect bounds = page.bounds;
      //CGRect frame = CGRectMake(_pageRegion.size.width * i, 0.0, _pageRegion.size.width, _pageRegion.size.height);
      //CGRect frame = CGRectMake(_pageRegion.size.width * i, _pageRegion.origin.y+_pageRegion.size.height , _pageRegion.size.width, _pageRegion.size.height);
      //page.frame = frame;
      //page.bounds = bounds;
      [ scrollView addSubview: page ];
     }
     return;
    }

    /* For more than 3 views, add them all hidden, layout according to page */
    for(int i=0;i<[ _pages count];i++) {
     UIView *page = [ _pages objectAtIndex: i ];
     //CGRect bounds = page.bounds;
     //CGRect frame = CGRectMake(0.0, 0.0, _pageRegion.size.width, _pageRegion.size.height);
     //CGRect frame = CGRectMake(0.0, _pageRegion.origin.y+_pageRegion.size.height, _pageRegion.size.width, _pageRegion.size.height);
     //page.frame = frame;
     //page.bounds = bounds;
     page.hidden = YES;
     [ scrollView addSubview: page ];
    }
    [ self layoutScroller ];
}

- (void)layoutScroller {
    UIView *page;
    CGRect bounds, frame;
    int pageNum = [ self getCurrentPage ];

    if ([ _pages count ] <= 3)
     return;

    NSLog(@"Laying out scroller for page %d\n", pageNum);

    /* Left boundary */
    if (pageNum == 0) {
     for(int i=0;i<3;i++) {
      page = [ _pages objectAtIndex: i ];
      bounds = page.bounds;
      //frame = CGRectMake(_pageRegion.size.width * i, 0.0, _pageRegion.size.width, _pageRegion.size.height);
      frame = CGRectMake(_pageRegion.size.width * i, 0.0, _pageRegion.size.width, _pageRegion.size.height);
      NSLog(@"\tOffset for Page %d = %f\n", i, frame.origin.x);
      page.frame = frame;
      page.bounds = bounds;
      page.hidden = NO;
     }
     page = [ _pages objectAtIndex: 3 ];
     page.hidden = YES;
     _zeroPage = 0;
    }

    /* Right boundary */
    else if (pageNum == [ _pages count ] -1) {
     for(int i=pageNum-2;i<=pageNum;i++) {
      page = [ _pages objectAtIndex: i ];
      bounds = page.bounds;
      frame = CGRectMake(_pageRegion.size.width * (2-(pageNum-i)), 0.0, _pageRegion.size.width, _pageRegion.size.height);
      NSLog(@"\tOffset for Page %d = %f\n", i, frame.origin.x);
      page.frame = frame;
      page.bounds = bounds;
      page.hidden = NO;
     }
     page = [ _pages objectAtIndex: [ _pages count ]-3 ];
     page.hidden = YES;
     _zeroPage = pageNum - 2;
    }

    /* All middle pages */
    else {
     for(int i=pageNum-1; i<=pageNum+1; i++) {
      page = [ _pages objectAtIndex: i ];
      bounds = page.bounds;
      frame = CGRectMake(_pageRegion.size.width * (i-(pageNum-1)), 0.0, _pageRegion.size.width, _pageRegion.size.height);
      NSLog(@"\tOffset for Page %d = %f\n", i, frame.origin.x);
      page.frame = frame;
      page.bounds = bounds;
      page.hidden = NO;
     }
     for(int i=0; i< [ _pages count ]; i++) {
      if (i < pageNum-1 || i > pageNum + 1) {
       page = [ _pages objectAtIndex: i ];
       page.hidden = YES;
      }
     }
     //scrollView.contentOffset = CGPointMake(_pageRegion.size.width, 0.0);
     scrollView.contentOffset = CGPointMake(_pageRegion.size.width, 0.0);
     _zeroPage = pageNum-1;
    }
}

-(id)getDelegate {
    return _delegate;
}

- (void)setDelegate:(id)delegate {
    _delegate = delegate;
}

-(BOOL)getShowsPageControl {
    return _showsPageControl;
}

-(void)setShowsPageControl:(BOOL)showsPageControl {
    _showsPageControl = showsPageControl;
    if (_showsPageControl == NO) {
     //_pageRegion = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
     pageControl.hidden = YES;
     scrollView.frame = _pageRegion;
    } else {
     //_pageRegion = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height - 60.0);
     //_pageRegion = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
     pageControl.hidden = NO;
     scrollView.frame = _pageRegion;
    }
}

-(NSMutableArray *)getPages {
    return _pages;
}

-(void)setCurrentPage:(int)page {
//  [ scrollView setContentOffset: CGPointMake(0.0, 0.0) ];
    [ scrollView setContentOffset: CGPointMake(0.0,_pageRegion.origin.y) ];
    _zeroPage = page;
    [ self layoutScroller ];
    pageControl.currentPage = page;
}

-(int)getCurrentPage {
    return (int) (scrollView.contentOffset.x / _pageRegion.size.width) + _zeroPage;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    pageControl.currentPage = self.currentPage;
    [ self layoutScroller ];
    [ self notifyPageChange ];
}

-(void) pageControlDidChange: (id)sender 
{
    UIPageControl *control = (UIPageControl *) sender;
    if (control == pageControl) {
     //[ scrollView setContentOffset: CGPointMake(_pageRegion.size.width * (control.currentPage - _zeroPage), 0.0) animated: YES ];
     [ scrollView setContentOffset: CGPointMake(_pageRegion.size.width * (control.currentPage - _zeroPage), 0.0) animated: YES ];

    }
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
        [ self layoutScroller ];
        [ self notifyPageChange ];
}

-(void) notifyPageChange {
    if (self.delegate != nil) {
//      if ([ _delegate conformsToProtocol:@protocol(PageScrollViewDelegate) ]) {
      if ([ _delegate respondsToSelector:@selector(pageScrollViewDidChangeCurrentPage:currentPage:) ]) {
             [ self.delegate pageScrollViewDidChangeCurrentPage: (PageScrollView *)self currentPage: self.currentPage ];
      }
//   }
    }
}

@end

Here is my loadview method from my ViewController class:

- (void)loadView {
    [ super loadView ];

    /* Load demo images for pages */
    topPages = [ [ NSMutableArray alloc ] init ];
    for(int i = 0; i < 5; i++) {
     UIImage *image = [ [ UIImage alloc ] initWithData: [ NSData dataWithContentsOfURL: [ NSURL URLWithString: [ NSString stringWithFormat: @"http://www.zdziarski.com/demo/%d.png", i+1 ] ] ] ];
     UIImageView *page = [ [ UIImageView alloc ] initWithFrame: CGRectMake(0,0,320,160) ] ;
     page.image = image;
     //page.bounds = CGRectMake(0.0, 0.0, 320, 160);
     [ topPages addObject: page ];  
    }

    CGRect viewBounds = [ [ UIScreen mainScreen ] applicationFrame ];
    viewBounds.origin.y = 0.0;

    portraitTopView = [ [ PageScrollView alloc ] initWithFrame: CGRectMake(0,0,320,160) ];
    portraitTopView.pages = topPages;
    portraitTopView.delegate = self;
    //portraitTopView.showsPageControl = NO;

    middlePages = [ [ NSMutableArray alloc ] init ];
    for(int i = 0; i < 5; i++) {
     UIImage *image = [ [ UIImage alloc ] initWithData: [ NSData dataWithContentsOfURL: [ NSURL URLWithString: [ NSString stringWithFormat: @"http://www.zdziarski.com/demo/%d.png", i+1 ] ] ] ];
     UIImageView *page = [ [ UIImageView alloc ] initWithFrame: CGRectMake(0,0,320,160) ] ;
     page.image = image;
     //page.bounds = CGRectMake(0.0, 0.0, 320, 160);
     [ middlePages addObject: page ];
    }

    portraitMiddleView = [ [ PageScrollView alloc ] initWithFrame: CGRectMake(0,80,320,160) ];
    portraitMiddleView.pages = middlePages;
    portraitMiddleView.delegate = self;
    //portraitMiddleView.showsPageControl = NO;

    bottomPages = [ [ NSMutableArray alloc ] init ];
    for(int i = 0; i < 5; i++) {
     UIImage *image = [ [ UIImage alloc ] initWithData: [ NSData dataWithContentsOfURL: [ NSURL URLWithString: [ NSString stringWithFormat: @"http://www.zdziarski.com/demo/%d.png", i+1 ] ] ] ];
     UIImageView *page = [ [ UIImageView alloc ] initWithFrame: CGRectMake(0,0,320,160) ] ; 
     page.image = image;
     //page.bounds = CGRectMake(0.0, 0.0, 320, 160);
     [ bottomPages addObject: page ];  
    }

    portraitBottomView = [ [ PageScrollView alloc ] initWithFrame: CGRectMake(0,160,320,160) ];
    portraitBottomView.pages = bottomPages;
    portraitBottomView.delegate = self;
    //portraitBottomView.showsPageControl = NO;


    portraitView = [ [ UIView alloc ] initWithFrame: viewBounds ];
    [ portraitView addSubview: portraitTopView ];
    [ portraitView addSubview: portraitMiddleView ];
    [ portraitView addSubview: portraitBottomView ];

    landscapeTextView = [ [ UITextView alloc ] initWithFrame: viewBounds ];
    landscapeTextView.text = woahDizzy;

    self.view = portraitView;
}

My problem is that the scroll areas (the parts of the screen I touch) don't seem to match up with the images. To get the images to display correctly, I'm using:

portraitTopView = [ [ PageScrollView alloc ] initWithFrame: CGRectMake(0,0,320,160) ];
portraitMiddleView = [ [ PageScrollView alloc ] initWithFrame: CGRectMake(0,80,320,160) ];
portraitBottomView = [ [ PageScrollView alloc ] initWithFrame: CGRectMake(0,160,320,160) ];

However, those numbers don't seem right to me. I feel the y values should be 0,160, and 320 respectively because I have 3 images that are each 160 pixels tall and (3x160=480), but when I set the y values to 0,160, and 320 the top image is at the top of the screen, the middle image is where the bottom image should be and the bottom image is way off the screen. But when I have the y values set to 0,80, and 160 the images display correctly, but the scroll areas do work (I have a gut feeling that scroll areas or touch areas whatever they are called are being set above the images).

Does anyone have any ideas?

+1  A: 

I think things could be a lot easier if you did not try and construct the layout manually. Have a look at this:

Create your UIScrollView (viewBase is an instance of UIScrollView)

int imgWidth = 160;
int numImages = 3;
int scrollWidth  = imgWidth * numImages;

int imgHeight = 160;
int numRows = 3;
int scrollHeight = imgHeight * numRows;

viewBase.contentSize = CGSizeMake(scrollWidth, scrollHeight);
// Set anything else you need on viewbase

Then when you add your elements to the UIScrollView, do similar calculations.

// Add this View to the far left of the scroller.
webViewOne = [[UIWebView alloc] initWithFrame: CGRectMake(viewBase.frame.origin.x, viewBase.frame.origin.y, viewBase.frame.size.width, viewBase.frame.size.height)];
[viewBase addSubview:webViewOne];
[webViewOne release];

// Add this View tot he right of webViewOne (Note the offset on the X coordinate)
webViewTwo = [[UIWebView alloc] initWithFrame: CGRectMake((viewBase.frame.size.width * 1), viewBase.frame.origin.y, viewBase.frame.size.width, viewBase.frame.size.height)];
[viewBase addSubview:webViewTwo];
[webViewTwo release];

This allows all the sizing and layout to be relative to the underlying UIScrollView rather than trying to control things pixel-by-pixel.

MystikSpiral
Will this allow for 3 independently scrolling rows of images? I want to be able to see image 1 in the top row, image 3 in the middle row and image 2 in the bottom row, or any other combination. Also I was trying to use PageScrollView because I'll have hundreds of images.
Yes, the UIScrollView is just a large "canvas". You set items on it using calculations and pixels. The code above creates a UIScrollView that is twice the width of a device's screen and then sets two UIWebViews side-by-side (by offsetting the plot on the X-Axis of the second View. By offsetting the X AND the Y plot, you create columns and rows. You just need to be able to track the spots on the virtual "grid" you are creating. That is why I created variables for width, height, etc... Easier to track your layout.
MystikSpiral
I think I get it. You're suggesting one large canvas with 3 independently horizontally moving frames instead of my 1 static frame with 3 moving canvases? I like the idea, but I don't really understand how you get around the memory issue of loading up hundreds of images all at once?
Do not load up all the images at once. Since a user can only scroll in one of four directions, implement the logic to load up the image above, below and to the left and right of their current location. It will mean you load at most three images per scroll.
MystikSpiral