Is UIPageControl Useless By Itself?

2019-01-13 14:51发布

问题:

I've been looking into using the UIPageControl for a scrolling part of an application, and I was wondering how it works. The docs show methods for changing pages, setting the number of pages etc., but by itself the page control doesn't encompass any kind of scroll view.

Are the UIPageControl and UIScrollView classes supposed to be used in conjunction when one wants to show the page indicator (like on the home screen of the iPhone with the app pages)?

回答1:

The paging bit is actually done by setting the paging property on the UIScrollView. The page control is simply a useful UI construct, but doesn't actually do anything to do with paging itself.



回答2:

I've written a class (PagedView) which works just like UITableView and merges the concepts of the UIPageControl and UIScrollView in a manner used for example on the iPhone home screen.

The concept is basically as follows: you need to implement PagedViewDelegate to return the number of pages and a view for each page of you PagedView. Reusing of views works the same as in UITableView. Use interface builder to connect the scrollview and pageControl outlets.

Please let me know if you find this class useful.

.h-file:

//
//  PagedView.h
//
//  Created by Werner Altewischer on 22/10/10.
//  Copyright 2010 werner-it.com. All rights reserved.
//

@protocol ReusableObject

- (NSString *)reuseIdentifier;
- (void)prepareForReuse;

@end

@class PagedView;

@protocol PagedViewDelegate

- (NSUInteger)numberOfPagesInPagedView:(PagedView *)view;
- (UIView *)pagedView:(PagedView *)view viewForPageAtIndex:(NSUInteger)page;

@end

@interface PagedView : UIView<UIScrollViewDelegate> {
    IBOutlet UIScrollView *scrollView;
    IBOutlet UIPageControl *pageControl;
    NSMutableDictionary *pageViewDictionary;
    NSMutableDictionary *reuseViewDictionary;

    IBOutlet id <PagedViewDelegate> delegate;
}

@property (nonatomic, assign) IBOutlet id <PagedViewDelegate> delegate;

- (UIView<ReusableObject> *)dequeueReusableViewWithIdentifier:(NSString *)identifier;

- (void)scrollToPageAtIndex:(NSUInteger)pageIndex animated:(BOOL)animated;
- (NSInteger)indexForSelectedPage;

- (CGSize)pageSize;
- (void)reloadData;

@end

.m-file:

//
//  PagedView.m
//
//  Created by Werner Altewischer on 22/10/10.
//  Copyright 2010 werner-it.com. All rights reserved.
//



#define TT_RELEASE_SAFELY(__POINTER) { [__POINTER release]; __POINTER = nil; }

    @interface PagedView (Private)

    - (NSUInteger)pageCount;

    - (UIView *)loadViewForIndex:(NSUInteger)pageIndex;
    - (void)unloadViewForIndex:(NSUInteger)pageIndex;
    - (void)loadViewsForVisiblePages:(BOOL)reloadData;  
    - (UIView *)viewForIndex:(NSUInteger)pageIndex;

    @end

    @implementation PagedView

    @synthesize delegate;

    - (void)dealloc {
        TT_RELEASE_SAFELY(pageViewDictionary);
        TT_RELEASE_SAFELY(reuseViewDictionary);
        TT_RELEASE_SAFELY(scrollView);
        TT_RELEASE_SAFELY(pageControl);
        [super dealloc];
    }

    - (CGSize)pageSize {
        return scrollView.frame.size;
    }

    - (void)reloadData {
        if (!pageViewDictionary) {
            //First time initialization 
            pageViewDictionary = [NSMutableDictionary new];
            reuseViewDictionary = [NSMutableDictionary new];
            [pageControl addTarget:self action:@selector(pageChanged:) forControlEvents:UIControlEventValueChanged];
            scrollView.delegate = self;
            scrollView.pagingEnabled = YES;
        }

        CGSize size = self.pageSize;

        NSUInteger numberOfPages = self.pageCount;  
        pageControl.numberOfPages = MAX(1, numberOfPages);
        [scrollView setContentSize:CGSizeMake(size.width * numberOfPages, size.height)];
        pageControl.currentPage = self.indexForSelectedPage;

        pageControl.hidden = (numberOfPages == 0);

        [self loadViewsForVisiblePages:YES];
    }

    - (void)layoutSubviews {
        if (!pageViewDictionary) {
            [self reloadData];
        }
    }

    - (void)scrollToPageAtIndex:(NSUInteger)pageIndex animated:(BOOL)animated {
        if (pageIndex < self.pageCount) {
            CGSize size = scrollView.frame.size;
            CGRect rect = CGRectMake(size.width * pageIndex, 0, size.width, size.height);
            [scrollView scrollRectToVisible:rect animated:animated];
        }
    }

    - (NSInteger)indexForSelectedPage {
        CGFloat cx = scrollView.contentOffset.x;
        NSUInteger index = (NSUInteger)(cx / scrollView.frame.size.width);
        if (index >= self.pageCount) {
            index = NSNotFound;
        }
        return index;
    }

    #pragma mark -
    #pragma mark UIScrollViewDelegate implementation

    - (void)scrollViewWillBeginDragging:(UIScrollView *)theScrollView {
        theScrollView.userInteractionEnabled = NO; 
    }

    - (void)scrollViewDidEndDecelerating:(UIScrollView *)theScrollView {
        if (theScrollView == scrollView) {
            pageControl.currentPage = self.indexForSelectedPage;
            [self loadViewsForVisiblePages:NO];
            theScrollView.userInteractionEnabled = YES; 
        }
    }

    - (void)pageChanged:(UIPageControl *)thePageControl {
        if (pageControl == thePageControl) {
            [self scrollToPageAtIndex:pageControl.currentPage animated:YES];
            [self loadViewsForVisiblePages:NO];
        }
    }

    - (UIView<ReusableObject> *)dequeueReusableViewWithIdentifier:(NSString *)identifier {
        UIView<ReusableObject> *v = [[[reuseViewDictionary objectForKey:identifier] retain] autorelease];
        if (v) {
            [v prepareForReuse];
            [reuseViewDictionary removeObjectForKey:identifier];
        }
        return v;
    }

    @end


    @implementation PagedView (Private)

    - (NSUInteger)pageCount {
        return [self.delegate numberOfPagesInPagedView:self];
    }

    - (UIView *)viewForIndex:(NSUInteger)pageIndex {
        id key = [NSNumber numberWithUnsignedInteger:pageIndex];
        return [pageViewDictionary objectForKey:key];
    }

    - (UIView *)loadViewForIndex:(NSUInteger)pageIndex {
        id key = [NSNumber numberWithUnsignedInteger:pageIndex];
        UIView *v = [pageViewDictionary objectForKey:key];
        if (!v) {
            CGSize size = self.pageSize;
            UIView *v = [self.delegate pagedView:self viewForPageAtIndex:pageIndex];
            if (v) {
                v.frame = CGRectMake(pageIndex * size.width, 0, size.width, size.height);
                [scrollView addSubview:v];      
                [pageViewDictionary setObject:v forKey:key];
            }
        } 
        return v;
    }

    - (void)unloadViewForIndex:(NSUInteger)pageIndex {
        id key = [NSNumber numberWithUnsignedInteger:pageIndex];
        UIView *v = [pageViewDictionary objectForKey:key];
        if (v) {
            if ([v conformsToProtocol:@protocol(ReusableObject)]) {
                NSString *reuseIdentifier = [(id <ReusableObject>)v reuseIdentifier];
                [reuseViewDictionary setObject:v forKey:reuseIdentifier];
            }
            [v removeFromSuperview];
            [pageViewDictionary removeObjectForKey:key];
        }

    }

    - (void)loadViewsForVisiblePages:(BOOL)reloadData {

        //load the selected view and the one in front and behind

        NSUInteger selectedPage = self.indexForSelectedPage;
        NSUInteger numberOfPages = self.pageCount;

        int intSelectedPage = (selectedPage == NSNotFound) ? -2 : (int)selectedPage;

        //Find the max number present in the pageViewDictionary
        NSUInteger existingPageCount = 0;
        for (NSNumber *key in pageViewDictionary) {
            if ([key unsignedIntegerValue] >= existingPageCount) {
                existingPageCount = [key unsignedIntegerValue] + 1;
            }
        }

        for (int i = 0; i < MAX(numberOfPages, existingPageCount); ++i) {
            if (i >= numberOfPages || 
                i < (intSelectedPage - 1) ||
                i > (intSelectedPage + 1)) {
                [self unloadViewForIndex:i];
            } else {
                if (reloadData) {
                    //Unload the view if we're reloading all the data
                    [self unloadViewForIndex:i];
                }
                [self loadViewForIndex:i];
            }
        }

        [reuseViewDictionary removeAllObjects];
    }

    @end


回答3:

UIPageControl does two things: (1) it displays dots, including a highlighted dot for the currently selected page, and (2) it generates a UIControlEventValueChanged event when the user taps on it. Tap on the right side of the control to page to the right; tap on the left side to page to the left. You should implement this behavior to be consistent with the HIG!

To trap the event, add:

[myUiPageControl addTarget:self action:@selector(pageChanged:) forControlEvents:UIControlEventValueChanged];

and that delegate function as well:

-(void)pageChanged:(UIPageControl*)thePageControl;

The name can be whatever you want; I used "pageChanged" for example. The callback signature can be pageChanged, pageChanged:, or pageChanged:forEvent:. This callback function should cause your scrollview (or what have you) to update.

Paging, itself, is usually done by using a UIScrollView with paging enabled and scrollbars hidden. Tell the scrollview to use paging and flicks magically move page by page; make sure you trap that change to update the currentPage property of the UIPageControl.



回答4:

You're correct--UIPageControl doesn't draw anything by itself other than the dots at the bottom of the page; you need another view to do that.



回答5:

To use Werner Altewischer class add PagedViewDelegate to interface and complete your class as shown below:

@interface ViewController (){
    //header view
    UIPageControl* headPageControl;
    UIScrollView* headScrollView;
    PagedView* headerView;
    NSMutableArray* headerViews;
}
//header view
-(void)initHeader;
-(void)setHeaderViews:(NSMutableArray*)views;
-(void)addHeaderView:(UIView*)view;
-(void)popHeader;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self initHeader];
    [self popHeader];  
}

-(void)initHeader
{
    float frameWidth = self.view.frame.size.width;

    headPageControl = [[UIPageControl alloc]initWithFrame:CGRectMake(0, HEADER_HEIGHT-PAGE_CONTROL_HEIGHT, frameWidth, PAGE_CONTROL_HEIGHT)];
    [headPageControl setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0.5]];

    headerViews = [NSMutableArray array];

    headScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, frameWidth, HEADER_HEIGHT)];
    headScrollView.showsHorizontalScrollIndicator = NO;

    headerView = [[PagedView alloc] initWithFrame:CGRectMake(0, 0, frameWidth, HEADER_HEIGHT)];
    headerView.delegate = self;
    headerView.pageControl = headPageControl;
    headerView.scrollView = headScrollView;

    [headerView addSubview:headScrollView];
    [headerView addSubview:headPageControl];

    [headerView setBackgroundColor:[UIColor darkGrayColor]];
    [self.view addSubview:headerView];
}

-(NSUInteger)numberOfPagesInPagedView:(PagedView *)view
{
    //NSLog(@"number of pages %i", headPageControl.numberOfPages);
    return headPageControl.numberOfPages;
}

-(UIView*)pagedView:(PagedView *)view viewForPageAtIndex:(NSUInteger)page
{
    //NSLog(@"open page %i", page);
    return [headerViews objectAtIndex:page];
}

-(void)setHeaderViews:(NSMutableArray*)views
{
    headerViews = views;
    headPageControl.numberOfPages = headerViews.count;
}

-(void)addHeaderView:(UIView*)view
{
    [headerViews addObject:view];
    headPageControl.numberOfPages = headerViews.count;
}

-(void)popHeader
{
    float frameWidth = self.view.frame.size.width;
    CGRect _frame = CGRectMake(0, 0, frameWidth, HEADER_HEIGHT);
    UIView* v = [[UIView alloc] initWithFrame:_frame];
    [v setBackgroundColor:[UIColor greenColor]];
    [self addHeaderView:v];

    UIView* v0 = [[UIView alloc] initWithFrame:_frame];
    [v0 setBackgroundColor:[UIColor purpleColor]];
    [self addHeaderView:v0];

    UIView* v1 = [[UIView alloc] initWithFrame:_frame];
    [v1 setBackgroundColor:[UIColor yellowColor]];
    [self addHeaderView:v1];
}