How to create a UIScrollview where it shows a portion of a view either side?

732 Views Asked by At

I'm trying to create a UIScrollView where it will show a portion of the views contained in the UIScrollView on either side.

Here is a picture;

Example of UIScrollview

The picture shows a grey area which is the parent or main view. The purple area is the UIScrollView. The white area is the view which contains the image of the box, the ready title and hurry button.

The problem I am having is that this white area is taking up the whole space and there is no visual indication on the screen that you can even scroll, let alone there is any more content.

Ideally, what I'm after is that there will be multiple white subviews within the UIScrollView so that you can see a portion of each subview on either side, a bit like this:

concept

In this concept you will see that there is a portion of the subview cut-off, but most of all you will see multiple subviews in the UIScrollView.

The sub-view (the one contained in the white section) is 75 (width) x 160 (height).

I cannot seem to get this to work however. I've tried setting clipToBounds both to yes and no, and I've tried forcing the content size; both of which give unexpected results.

How do I make it show more than 1 UIView on the UIScrollView, or portions of UIViews on the UIScrollView?

Many thanks.

Here is my code;

// Code follows
- (void)viewDidLoad
{
    // Scrollview
    int recordsFound = 6; // 6 products
    float scrollViewWidth = (self.scrollView.frame.size.width * recordsFound);
    float scrollViewHeight = (self.scrollView.frame.size.height);

    [self.scrollView setContentSize:CGSizeMake(scrollViewWidth, scrollViewHeight)];    
    [self.scrollView setClipsToBounds:YES];
    [self.scrollView setScrollEnabled:YES];
    [self.scrollView setPagingEnabled:YES];
    [self.scrollView setDirectionalLockEnabled:YES];
    [self.scrollView setUserInteractionEnabled:YES];
    [self.scrollView setShowsHorizontalScrollIndicator:YES];
    [self.scrollView setShowsVerticalScrollIndicator:NO];
    [self.scrollView setScrollEnabled:YES];
    [self.scrollView setDelegate:self];


    // view controllers are created lazily
    // in the meantime, load the array with placeholders which will be replaced on demand
    NSMutableArray *controllers = [[NSMutableArray alloc] init];
    for (unsigned i = 0; i < recordsFound; i++)
    {
        [controllers addObject:[NSNull null]];


    } // next

    //self.scrollView.bounds = CGRectMake(0, 0, 100, scrollView.frame.size.height);


    self.viewControllers = controllers;
    [controllers release];


    [self loadScrollViewWithPage:0];
    [self loadScrollViewWithPage:1];
}



-(void)loadScrollViewWithPage:(int)page
{
    int numberOfRecords = 6;

    if (page < 0)
        return;
    if (page >= numberOfRecords)
        return;

    // replace the placeholder if necessary
    SecondPageVC *controller = [viewControllers objectAtIndex:page];
    if ((NSNull *)controller == [NSNull null])
    {
        controller = [[SecondPageVC alloc] initWithPageNumber:page];        
        [viewControllers replaceObjectAtIndex:page withObject:controller];
        [controller release];
    }


    // add the controller's view to the scroll view
    if (controller.view.superview == nil)
    {
        CGRect frame    = scrollView.frame;
        frame.origin.x  = frame.size.width * page;
        frame.origin.y  = 0;
        controller.view.frame = frame;
        [scrollView addSubview:controller.view];

    }


}

-(void)scrollViewDidScroll:(UIScrollView *)sender
{
    // We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
    // which a scroll event generated from the user hitting the page control triggers updates from
    // the delegate method. We use a boolean to disable the delegate logic when the page control is used.
    if (pageControlBeingUsed)
    {
        // do nothing - the scroll was initiated from the page control, not the user dragging
        return;
    }

    // Switch the indicator when more than 50% of the previous/next page is visible
    CGFloat pageWidth = scrollView.frame.size.width;
    int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;

    // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];

    // A possible optimization would be to unload the views+controllers which are no longer visible
}


- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 
{

}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{

}
1

There are 1 best solutions below

1
On

I think I have a solution now, its not perfect and needs customising for the paritial view that you see in the diagram.

I have got it working for the full view.

The way I did it was to create a view approx 210w x 280h and set it centered in the screen, the following code allows you to see a bit of each view (different colors) on each side.

I found "ScrollViewPagingExample" on Github (link)

// Card dimensions = 210 x 280

    // Scrollview    
    [_scrollView setClipsToBounds:NO];
    [_scrollView setPagingEnabled:YES];
    [_scrollView setShowsHorizontalScrollIndicator:NO];
    [_scrollView setAlwaysBounceHorizontal:YES];

    CGFloat contentOffset = 0.0f;


    for (int i = 0; i <5; i++ )
    {
        CGRect someFrame = CGRectMake(contentOffset, 0.0f, _scrollView.frame.size.width, _scrollView.frame.size.height);

        // Create view
        UIView *singleView = [[UIView alloc] initWithFrame:someFrame];

        switch (i) {
            case 0:
                [singleView setBackgroundColor:[UIColor greenColor]];
                break;
            case 1:
                [singleView setBackgroundColor:[UIColor darkGrayColor]];
                break;
            case 2:
                [singleView setBackgroundColor:[UIColor grayColor]];
                break;
            case 3:
                [singleView setBackgroundColor:[UIColor blueColor]];
                break;
            case 4:
                [singleView setBackgroundColor:[UIColor purpleColor]];
                break;
            default:
                //[singleView setBackgroundColor:[UIColor cyanColor]];
                break;
        }


        [singleView setContentMode:UIViewContentModeCenter];

        // Label
        UILabel *lblCardView = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
        [lblCardView setText:[NSString stringWithFormat:@"Card %d", i]]; 
        [lblCardView setTextColor:[UIColor whiteColor]];
        [lblCardView setBackgroundColor:[UIColor clearColor]];

        [singleView addSubview:lblCardView];

        // Add to scrollview        
        [_scrollView addSubview:singleView];

        // Memory management
        [singleView release];
        [lblCardView release];


        // Update contentOffset with padding
        contentOffset += singleView.frame.size.width +35;

        _scrollView.contentSize = CGSizeMake(contentOffset, _scrollView.frame.size.height);

    } // next