需要帮助在故事板上的 UIScrollView / UIPageControl 中推送视图
Posted
技术标签:
【中文标题】需要帮助在故事板上的 UIScrollView / UIPageControl 中推送视图【英文标题】:Need help for pushing a view in UIScrollView / UIPageControl on storyboard 【发布时间】:2012-01-03 15:17:38 【问题描述】:我正在尝试在情节提要上使用 UIPageControl 实现 UIScrollView 以显示多个视图。我发现的所有示例、教程或信息都使用 xib 而不是故事板。我试图改编 Apple 的示例代码,但我遗漏了一些东西。如您所见,这很简单。
但是,当我测试 if (controller.view.superview == nil) 我推送视图的地方(见下文前半部分代码)。
或者只有 UIScrollView 和 UIPageControl 工作,我无法在 UIScrollView 中推送视图:
我知道这是一个简单的问题,但是我花了很多时间来解决这个问题,我们将不胜感激。
我发布了完整的代码,因为当它像一种教程一样解决时可能会对某人有所帮助。如果/需要时,我会根据贡献更正代码。
提前致谢
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
【问题讨论】:
如果有人知道教程的链接使用故事板,我将不胜感激。 这是一个很好的教程:raywenderlich.com/10518/… 【参考方案1】:发生无法识别的选择器错误是因为您在 loadScrollViewWithPage 的 if 语句中重新声明了局部变量“控制器”:因为您这样做了,所以新的控制器变量仅存在于 if 语句的范围内,并且当您退出时if 语句该范围之外的控制器变量仍然是 NSNull。
您可以通过删除 if 语句中的变量定义来修复:
if ((NSNull *)controller == [NSNull null])
controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];
[controller initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
【讨论】:
感谢您的宝贵时间和回答。但是,我很确定错误不是来自这里。如果控制器变量为空,则在 if 语句中定义控制器变量。退出 if 语句时,它不再为空。MyViewController *controller = [viewControllers objectAtIndex:page];
中对变量的第一次引用并不是真正的声明(如果我错了,请纠正我)。此语句在 Apple 示例代码中运行良好。再次感谢。
仔细检查 Apple PageControl 示例中的代码:developer.apple.com/library/ios/#samplecode/PageControl/… 您会看到它们没有在 if 语句中声明控制器变量。当您这样做时,您将覆盖 if 语句范围内的现有变量。 (是的,以MyViewController *
开头的变量名确实构成声明)
是的,我仔细检查过,你是对的。谢谢你。但是,我仍然无法使用 Storyboard 使其工作。我必须努力获得更多经验。我暂时放弃了,因为我在这上面花了太多时间。我将使用 XIB 管理它。再次感谢。【参考方案2】:
所以我遇到了和你一样的问题,但我只是做了一些可能效果很好的事情。
简短的回答是对 Jonkroll 关于在 if 语句中启动控制器的回答的补充。出于某种原因,我无法解释空占位符不为空,因此 if 语句不会像您在代码中指出的那样执行。所以我所做的是修改 if 并将 else 添加到相同的 if 并从情节提要加载 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 就可以了,但我不知道为什么需要这样做,或者最糟糕的是为什么实际上没有按我预期的那样工作.如果有人知道,请分享。
【讨论】:
顺便说一句,我知道这是很糟糕的编程,我可以摆脱 if 语句。实际发生的一件事是每次滚动页面时内存使用量都会达到最高值,并且我会收到导致崩溃的内存警告。如果有人知道如何将它与情节提要一起使用,我会全力以赴。【参考方案3】:首先,感谢这个帖子,它确实是一种教程或对我的参考。
我所做的一切几乎和你一样,除了在内容控制器中我输入了以下代码(基于@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 嵌入到导航控制器中。
这几乎就是一切。后来,我什至设法从 MyViewController 导航到另一个视图,一切正常。
如果您需要进一步的帮助,您可以在这里提问。
问候。
【讨论】:
【参考方案4】:当我尝试遵循 Apple Documents 中的 UIPageController 示例代码时,我遇到了同样的问题。因为我以编程方式完成了 MyViewController,所以我直接使用以下代码
if ((NSNull *)controller == [NSNull null])
MyViewController * controller = [[MyViewController alloc]init];
[viewControllers replaceObjectAtIndex:page withObject:controller];
我在 MyViewController 中什么也没做。
【讨论】:
以上是关于需要帮助在故事板上的 UIScrollView / UIPageControl 中推送视图的主要内容,如果未能解决你的问题,请参考以下文章