Defining scroll amount with UIScrollView and PageC

2019-08-27 17:01发布

问题:

For the application I am working on I need a scrollview with paging along with a number of views (in this case tableviews). Setting this up (using Apple's PageControl example) is easy enough and I have no problem making it work in regards to scrolling and snapping between pages. I want the tableviews to be almost like thumbnail representations of the tableviews in the scrollview. Once you click one it opens or zooms in to let the tableview cover the entire screen. Again, setting this up is not really a problem, I can make the views inside the scrollview any size I want and the clicking I'm sure I can get to work when I get to that part. :)

My Problem is this: In any given view, regardless of what page you happen to be on I want the next page and the previous page to be slightly visible on the right and left side respectively. Of course on the first page there would only be one more page shown on the right side, or left side for the last page. Again, by setting the x-origin of the views (tableviews) shown I can get the first page to look right. However, as soon as you scroll the problem becomes apparent, namely that the screen scrolls too far.

Breaking it down I guess my main issue is that I can't seem to change the amount of scrolling done when switching pages.

I'd appreciate any help I can get on this, thank you.

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : NSObject <UIApplicationDelegate, UIScrollViewDelegate> {
UIWindow *window;
UIView *background;
UIScrollView *scrollView;
UIPageControl *pageControl;
NSMutableArray *viewControllers;

// To be used when scrolls originate from the UIPageControl
// I've commented this out (and all references to it) since I've disabled user interaction with the UIPageControl
//BOOL pageControlUsed;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UIView *background;

@property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
@property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
@property (nonatomic, retain) NSMutableArray *viewControllers;

- (IBAction)changePage:(id)sender;

@end  

AppDelegate.m

#import "AppDelegate.h"
#import "MyViewController.h"

static NSUInteger kNumberOfPages = 4;

@interface AppDelegate (PrivateMethods)

- (void)loadScrollViewWithPage:(int)page;
- (void)scrollViewDidScroll:(UIScrollView *)sender;

@end

@implementation AppDelegate

@synthesize window, scrollView, pageControl, viewControllers, background;

- (void)dealloc {
[background release];
[viewControllers release];
[scrollView release];
[pageControl release];
[window release];
[super dealloc];
}

- (void)applicationDidFinishLaunching:(UIApplication *)application {

background.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Background.png"]];

// 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 < kNumberOfPages; i++) {
    [controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];

// a page is the width of the scroll view
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;

pageControl.numberOfPages = kNumberOfPages;
pageControl.currentPage = 0;

// pages are created on demand
// load the visible page
// load the page on either side to avoid flashes when the user starts scrolling
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
}

- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;

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

// add the controller's view to the scroll view
if (nil == controller.view.superview) {
    CGRect frame = CGRectMake(50, 50, 220, 330);
    frame.origin.x = 50 + (250 * page);
    frame.origin.y = 50;
    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 (pageControlUsed) {
    // 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;
pageControl.currentPage = page;

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

// At the begin of scroll dragging, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
//pageControlUsed = NO;
}

// At the end of scroll animation, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
//pageControlUsed = NO;
}

- (IBAction)changePage:(id)sender {
int page = pageControl.currentPage;

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

// update the scroll view to the appropriate page
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];

// Set the boolean used when scrolls originate from the UIPageControl. See       scrollViewDidScroll: above.
//pageControlUsed = YES;
}

@end

回答1:

It seems that you are setting the frame size of the scrollView and the frame of controller.view differently. The scrollView is an IBOutlet, that means that you are creating the scrollView usign the interface builder, but you are putting content using this lines:

CGRect frame = CGRectMake(50, 50, 220, 330);
frame.origin.x = 50 + (250 * page);
frame.origin.y = 50;
controller.view.frame = frame;
[scrollView addSubview:controller.view];

and putting it into the scrollView. If you do not want to see the previous/next page on the side of your scrollView, you have to change the numbers you are using at the first line. Try using `scrollView.frame.size or check the frame size of the scrollView you created from the IB and use them instead of magic numbers, which you should avoid.