问题描述
我正在尝试在故事板上使用UIPageControl实现UIScrollView以显示多个视图。我发现的所有示例,教程或信息都使用的是xib而不是故事板。我试图改编Apple的示例代码,但我遗漏了一些东西。如您所见,它非常简单。
I'm trying to implement UIScrollView with UIPageControl on storyboard to display multiple views. All the examples, tutorials or informations I found are using xib and not storyboard. I tried to adapt Apple's sample code but I'm missing something. As you can see, it is quite simple.
但是,我在ContentController.m中遇到错误 - [NSNull view]:无法识别的选择器发送到实例0x129acd8 当我测试 if(controller.view.superview == nil) 我推送视图时(见上半部分代码后见下文) 。
However, either I get an error in ContentController.m -[NSNull view]: unrecognized selector sent to instance 0x129acd8 when I test if (controller.view.superview == nil) where I push the view (see below after the first half of code).
或者只有UIScrollView和UIPageControl正在工作,我无法在UIScrollView中推送视图:
Or only the UIScrollView and UIPageControl are working and I am not able to push the view in the UIScrollView:
我知道这是一个简单的问题,但我花了很多时间在这上面,任何帮助将不胜感激。
I know it's a simple question, however I spent many hours on this and any help will be appreciated.
我发布了完整的代码,因为它可以帮助某人解决像一种教程。如果需要,我会根据贡献更正代码。
I post the full code as it may help someone when resolved like a kind of tutorial. I will correct the code if/when needed according to contributions.
提前致谢
AppDelegate .h
// Nothing special as I don't want to manage it through application delegate
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
AppDelegate.m
// Nothing special as I don't want to manage it through application delegate
#import "AppDelegate.h"
@implementation AppDelegate
@synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application { }
- (void)applicationDidEnterBackground:(UIApplication *)application { }
- (void)applicationWillEnterForeground:(UIApplication *)application { }
- (void)applicationDidBecomeActive:(UIApplication *)application { }
- (void)applicationWillTerminate:(UIApplication *)application { }
@end
ContentController.h
// From Apple's PageControl sample code
#import <UIKit/UIKit.h>
@interface ContentController : UIViewController <UIScrollViewDelegate> {
UIScrollView *scrollView;
UIPageControl *pageControl;
NSMutableArray *viewControllers;
BOOL pageControlUsed;
}
@property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
@property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
@property (nonatomic, retain) NSMutableArray *viewControllers;
- (IBAction)changePage:(id)sender;
@end
ContentController.m
// From Apple's PageControl sample code
#import "AppDelegate.h"
#import "ContentController.h"
#import "MyViewController.h"
// Private methods
@interface ContentController (PrivateMethods)
- (void)loadScrollViewWithPage:(int)page;
- (void)scrollViewDidScroll:(UIScrollView *)sender;
@end
@implementation ContentController
@synthesize scrollView, pageControl, viewControllers;
static NSUInteger kNumberOfPages = 6;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationController.navigationBar.hidden=YES;
// 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;
// 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)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.pageControl=nil;
self.pageControl = nil;
self.viewControllers = nil;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (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]) // <== *** HERE SEEMS TO BE THE ERROR ***
{
controller = [[MyViewController alloc] initWithPageNumber:page
initWithNibName:nil
bundle:nil];
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
// 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 (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;
}
- (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
MyViewController.h
#import <UIKit/UIKit.h>
@interface MyViewController : UIViewController {
UILabel *pageNumberLabel;
int pageNumber;
}
@property (nonatomic, retain) IBOutlet UILabel *pageNumberLabel;
- (id)initWithPageNumber:(int)page initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
@end
MyViewController.m
#import "MyViewController.h"
@implementation MyViewController
@synthesize pageNumberLabel;
- (void)loadView {
}
- (void)viewDidLoad {
[super viewDidLoad];
// Set the label and background color when the view has finished loading
pageNumberLabel.text = [NSString stringWithFormat:@"Page %d", pageNumber + 1];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
// Load the view nib and initialize the pageNumber ivar
// classic initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
// class adapted to init the page number
// /!\ be careful, I'm not sure it's working properly
- (id)initWithPageNumber:(int)page initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
pageNumber = page;
NSLog(@"pageNumber = %i", pageNumber);
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
@end
推荐答案
发生了无法识别的选择器错误,因为您在loadScrollViewWithPage的if语句中重新声明了本地变量controller:因为您这样做,新的控制器变量只存在于if语句的范围内,并且当您退出if语句,该范围之外的控制器变量仍为NSNull。
The unrecognized selector error is happening because you are redeclaring your local variable "controller" within the if statement in loadScrollViewWithPage: Because you are doing so, the new controller variable only exists within the scope of the if statement, and when you exit the if statement the controller variable outside that scope is still NSNull.
您可以通过删除if语句中的变量定义来解决:
You can fix by removing the variable definition inside the if statement:
if ((NSNull *)controller == [NSNull null])
{
controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];
[controller initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
这篇关于需要帮助在故事板上推送UIScrollView / UIPageControl中的视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!