需要帮助在故事板上的UIScrollView / UIPageControl中推送视图

时间:2012-01-03 15:17:39

标签: ios uiscrollview storyboard uipagecontrol

我正在尝试在故事板上使用UIPageControl实现UIScrollView以显示多个视图。我发现的所有示例,教程或信息都使用的是xib而不是故事板。我试图改编Apple的示例代码,但我遗漏了一些东西。如您所见,这很简单。

enter image description here

但是,当我测试 时,我在ContentController.m - [NSNull view]中发生错误:无法识别的选择器发送到实例0x129acd8 (controller.view.superview == nil) 我推动视图的位置(在上半部分代码后见下文)。

或者只有UIScrollView和UIPageControl工作,我无法在UIScrollView中推送视图:

enter image description here

我知道这是一个简单的问题,但是我花了很多时间在这上面,任何帮助都会受到赞赏。

我发布完整的代码,因为它可能会帮助某人解决像一种教程。如果需要,我会根据贡献更正代码。

提前致谢

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

4 个答案:

答案 0 :(得分:1)

发生了无法识别的选择器错误,因为您在loadScrollViewWithPage中的if语句中重新声明了本地变量“controller”:因为您这样做,新的控制器变量只存在于if语句的范围内,并且当您退出时if语句,该范围之外的控制器变量仍然是NSNull。

您可以通过删除if语句中的变量定义来修复:

if ((NSNull *)controller == [NSNull null])
{
    controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];
    [controller initWithPageNumber:page];

    [viewControllers replaceObjectAtIndex:page withObject:controller];
}

答案 1 :(得分:0)

所以我遇到的问题与你所遇到的完全相同,但我只是做了一些可能工作正常的事情。

简短回答是对Jonkroll关于在if语句中启动控制器的回答的补充。由于某种原因,我无法解释空占位符不为空,因此if语句不会像您在代码中指出的那样执行。所以我所做的就是修改if并在其中添加一个else,并从故事板中加载ViewController。

if ((NSNull *)controller == [NSNull null]) {
        controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];        
        [viewControllers replaceObjectAtIndex:page withObject:[controller initWithPageNumber:page]];
}
else {
        controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];        
        [viewControllers replaceObjectAtIndex:page withObject:[controller initWithPageNumber:page]];
}

我的其余代码与您的代码完全一样。我知道这不是有史以来最好的代码,我可能只是在没有if / else语句的情况下启动ViewController,但我没有答案为什么需要这个或者最坏的为什么实际上并没有像我期望的那样工作。如果有人知道请分享。

答案 2 :(得分:0)

首先,对于这个帖子来说,它确实是一种教程或对我的引用。

我做的一切几乎和你一样,除了内容控制器我把下面的代码(基于@ jonkroll的回答):

    if ((NSNull *)controller == [NSNull null]){
    controller = [[self.storyboard instantiateViewControllerWithIdentifier:@"MyViewController"] initWithPageNumber:page initWithNibName:nil bundle:nil];
    [viewControllers replaceObjectAtIndex:page withObject:controller];
}

MyViewController(在我的情况下有不同的名称,但它没有任何相关性)继承自UITableViewController,因为它是我需要的东西。 我的initWithPageNumber方法如下所示:

    - (id)initWithPageNumber:(int)page initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
pageNumber = page;
NSLog(@"PageNumber = %i", pageNumber);
return self;

}

添加了return.self,请注意。

最后一点是,我在导航控制器中嵌入了ContentController,通过编辑器&gt;嵌入 - >导航控制器。

这几乎就是一切。后来,我甚至设法从MyViewController导航到另一个视图,一切正常。

如果您需要进一步的帮助,可以在这里自由询问。

问候。

答案 3 :(得分:0)

当我尝试从Apple Documents跟踪UIPageController的示例代码时,我遇到了同样的问题。因为我以编程方式完成了MyViewController,所以我直接使用以下代码

if ((NSNull *)controller == [NSNull null])
{
  MyViewController * controller = [[MyViewController alloc]init];
  [viewControllers replaceObjectAtIndex:page withObject:controller];
}

我在MyViewController中没有改变任何内容。