我一直在研究将 UIPageControl 用于应用程序的滚动部分,我想知道它是如何工作的。文档显示了更改页面、设置页数等的方法,但页面控件本身不包含任何类型的滚动视图。
当一个人想要显示页面指示器时(比如在 iPhone 的主屏幕上显示应用程序页面),是否应该结合使用 UIPageControl 和 UIScrollView 类?
我一直在研究将 UIPageControl 用于应用程序的滚动部分,我想知道它是如何工作的。文档显示了更改页面、设置页数等的方法,但页面控件本身不包含任何类型的滚动视图。
当一个人想要显示页面指示器时(比如在 iPhone 的主屏幕上显示应用程序页面),是否应该结合使用 UIPageControl 和 UIScrollView 类?
我编写了一个类(PagedView),它的工作方式与 UITableView 类似,并以 iPhone 主屏幕上使用的方式合并了 UIPageControl 和 UIScrollView 的概念。
概念基本上是这样的:你需要实现PagedViewDelegate来返回你PagedView的页数和每页的视图。重用视图的工作方式与 UITableView 中的相同。使用 interface builder 连接 scrollview 和 pageControl 出口。
如果你觉得这门课有用,请告诉我。
.h 文件:
//
// 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 文件:
//
// 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
分页位实际上是通过在 UIScrollView 上设置分页属性来完成的。页面控件只是一个有用的 UI 结构,但实际上与分页本身没有任何关系。
UIPageControl 做了两件事:(1)它显示点,包括当前选定页面的突出显示点,以及(2)当用户点击它时它会生成一个 UIControlEventValueChanged 事件。点击控件右侧可向右翻页;点击左侧可向左翻页。您应该实现此行为以与 HIG 保持一致!
要捕获事件,请添加:
[myUiPageControl addTarget:self action:@selector(pageChanged:) forControlEvents:UIControlEventValueChanged];
以及该委托功能:
-(void)pageChanged:(UIPageControl*)thePageControl;
名字可以是任何你想要的;例如,我使用了“pageChanged”。回调签名可以是 pageChanged、pageChanged: 或 pageChanged:forEvent:。这个回调函数应该导致你的滚动视图(或者你有什么)更新。
分页本身通常是通过使用启用分页并隐藏滚动条的 UIScrollView 来完成的。告诉滚动视图使用分页和轻弹神奇地逐页移动;确保捕获该更改以更新 UIPageControl 的 currentPage 属性。
你是对的——除了页面底部的点之外,UIPageControl 不会自己绘制任何东西;你需要另一个视图来做到这一点。
要使用 Werner Altewischer 类,请将 PagedViewDelegate 添加到接口并完成您的类,如下所示:
@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];
}