点击 UIButton 后如何在 UIPageViewController 中添加新的视图控制器?

Posted

技术标签:

【中文标题】点击 UIButton 后如何在 UIPageViewController 中添加新的视图控制器?【英文标题】:How to add a new view controller in UIPageViewController after UIButton was tapped? 【发布时间】:2012-11-04 20:42:07 【问题描述】:

我想知道如何实现会影响 UIPageViewController 模型的按钮。例如,我希望我的 UIPageViewController 模型数组在其中加载单个对象,当点击按钮时,将添加一个新的视图控制器,并自动翻转页面或类似的东西(如在 Notes 应用程序中)。删除用户正在查看的当前对象也是如此。

到目前为止,我尝试在我的根视图控制器中实现一些 IBAction,但到目前为止没有运气。

这是我实现 ViewController 类的方法:

@implementation ViewController

@synthesize modelArray = _modelArray;
@synthesize pageVC = _pageVC;

#pragma mark - UIPageViewControllerDataSource Methods

// Returns the view controller before the given view controller. (required)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
      viewControllerBeforeViewController:(UIViewController *)viewController

    NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentVC *)viewController     labelContents]];
    if(currentIndex==0)
        return nil;

    ContentVC *cVC = [[ContentVC alloc] init];
    cVC.labelContents = [self.modelArray objectAtIndex:currentIndex-1];
    return cVC;


// Returns the view controller after the given view controller. (required)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
   viewControllerAfterViewController:(UIViewController *)viewController

    NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentVC *)viewController labelContents]];
    if(currentIndex==self.modelArray.count-1)
        return nil;

    ContentVC *cVC = [[ContentVC alloc] init];
    cVC.labelContents = [self.modelArray objectAtIndex:currentIndex+1];
    return cVC;


#pragma mark - UIPageViewControllerDelegate Methods
// Returns the spine location for the given orientation.
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
               spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation

    if(UIInterfaceOrientationIsPortrait(orientation))
        // Set the array with only 1 view controller
        UIViewController *currentVC = [self.pageVC.viewControllers objectAtIndex:0];
        NSArray *viewControllers = [NSArray arrayWithObject:currentVC];
        [self.pageVC setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];

        // Set the doubleSided property to NO
        self.pageVC.doubleSided = NO;
        // Return the spine location
        return UIPageViewControllerSpineLocationMin;
     else  // if landscape
        NSArray *viewControllers = nil;
        ContentVC *currentVC = [self.pageVC.viewControllers objectAtIndex:0];
        NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentVC *)viewControllers labelContents]];
        if(currentIndex==0 || currentIndex %2 == 0)
            UIViewController *nextViewController = [self pageViewController:self.pageVC viewControllerAfterViewController:currentVC];
            viewControllers = [NSArray arrayWithObjects:currentVC, nextViewController, nil];
         else 
            UIViewController *previousVC = [self pageViewController:self.pageVC viewControllerBeforeViewController:currentVC];
            viewControllers = [NSArray arrayWithObjects:previousVC, currentVC, nil];
        
        // Set the view controllers as property of the UIPageViewController
        [self.pageVC setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
        return UIPageViewControllerSpineLocationMid;
    



#pragma mark - View lifecycle

- (void)viewDidLoad

    [super viewDidLoad];

    // Instantiate the model array
    self.modelArray = [[NSMutableArray alloc] init];
    [self.modelArray addObject:@"Page One"];
    NSLog(@"%@", self.modelArray);

    // Instantiate the UIPageViewController
    self.pageVC = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];

    // Assign the delegate and datasource as self
    self.pageVC.delegate = self;
    self.pageVC.dataSource = self;

    // Set the initial view controllers
    ContentVC *cVC = [[ContentVC alloc] initWithNibName:@"ContentVC" bundle:nil];
    cVC.labelContents = [self.modelArray objectAtIndex:0];
    NSArray *viewControllers = [NSArray arrayWithObject:cVC];
    [self.pageVC setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

    // Add the pageViewController as the childViewController
    [self addChildViewController:self.pageVC];

    // Add the view of pageViewController  to the currentView
    [self.view addSubview:self.pageVC.view];

    // Call didMoveToParentViewController: of the childViewController, the UIPageViewController instance in our case.
    [self.pageVC didMoveToParentViewController:self];

    // Assign the gestureRecognizers property of the pageViewController to the view's gestureRecognizers property
    self.view.gestureRecognizers = self.pageVC.gestureRecognizers;


非常感谢!

【问题讨论】:

【参考方案1】:

UIPageViewController 使用数据源获取下一个或上一个视图控制器。因此,解决方案是在触摸按钮后将新视图控制器添加到数据源。

假设您的数据源实现为NSArray(有关此示例,请参见 XCode 的基于页面的应用程序模板),您只需将新 vc 添加到该数组并通过调用 [UIPageViewController setViewControllers:direction:animated:completion:] 设置新 vc。

由于您没有提供有关如何实现层次结构的任何详细信息,因此我无法更具体。

编辑:

现在我有了一些代码,这就是我要做的:

首先我不会保存labelContents,而是将视图控制器本身保存在modelArray 中。除此之外,您当前的设计在每次更改页面时都会创建一个新的ContentVC。这是很多不必要的开销。以下是我将如何实现它:

(顺便说一句,你应该想一个比labelContents更具描述性的名字。现在如果只有一个标签可能还好,但如果你以后添加更多标签呢?)

- (ContentVC *)contentViewControllerWithLabelContents:(NSString *)labelContents

    ContentVC *vc = [[ContentVC alloc] initWithNibName:@"ContentVC" bundle:nil];
    vc.labelContents = labelContents;
    return vc;


- (void)viewDidLoad

    NSMutableArray *vcs = [NSMutableArray array];
    [vcs addObject:[self contentViewControllerWithLabelContents:@"Page One"]];
    [vcs addObject:[self contentViewControllerWithLabelContents:@"Page Two"]];
    //...
    self.modelArray = vcs;



#pragma mark - UIPageViewControllerDataSource Methods

// Returns the view controller before the given view controller. (required)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
      viewControllerBeforeViewController:(UIViewController *)viewController

    NSUInteger currentIndex = [self.modelArray indexOfObject:viewController];
    if(currentIndex == 0)
        return nil;

    ContentVC *cVC = [self.modelArray objectAtIndex:currentIndex - 1];
    return cVC;


// Returns the view controller after the given view controller. (required)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
   viewControllerAfterViewController:(UIViewController *)viewController

    NSUInteger currentIndex = [self.modelArray indexOfObject:viewController];
    if(currentIndex == self.modelArray.count - 1)
        return nil;

    ContentVC *cVC = [self.modelArray objectAtIndex:currentIndex + 1];
    return cVC;

您评论中的代码:

- (IBAction)newButtonTapped:(id)sender 
 
    if(sender) 
        [self.modelArray addObject:@"Page Two"]; 
        ContentVC *newVC = [[ContentVC alloc] initWithNibName:@"ContentVC" bundle:nil]; 
        NSArray *arr = [NSArray arrayWithObject:newVC]; 
        [self.pageVC setViewControllers:arr direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil]; 
        NSLog(@"%@", self.modelArray); 
     

不起作用,因为您没有设置 labelContentsContentVC。因此,您基本上最终会调用[self.modelObject indexOfObject:nil][self.modelObject indexOfObject:@""],具体取决于您对labelContents 的实现。由于它们都不在数组中,因此调用返回 NSNotFound,在 64 位系统上被转换为 NSIntegerMax,即 2147483647。稍后您尝试在索引NSIntegerMax - 1 处访问您的modelArray,这会引发NSRangeException

所以可以通过在您的 newButtonTapped: 方法中设置 labelContents 或者如果您按照我的建议重新设计您的代码来解决这个问题:

- (IBAction)newButtonTapped:(id)sender 
 
    if(sender) 
        ContentVC *newVC = [self contentViewControllerWithLabelContents:@"Page Two"]; 
        [self.modelArray addObject:newVC];

        NSArray *newVCs = nil;

        if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) || self.modelArray.count == 1)
            newVCs = @[newVC];
        else
        
            NSUInteger secondLastIndex = self.modelArray.count - 2;
            id previousVC = [self.modelArray objectAtIndex:secondLastIndex];
            newVCs = @[previousVC, newVC];
        

        [self.pageVC setViewControllers:newVCs direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
     

【讨论】:

嗨托比!谢谢回复。我已经用我的一些代码更新了我的问题。也许这能让你更清楚地了解我想要完成什么以及如何完成? 所以我尝试了这个 - (IBAction)newButtonTapped:(id)sender if(sender) [self.modelArray addObject:@"Page Two"]; ContentVC *newVC = [[ContentVC alloc] initWithNibName:@"ContentVC" bundle:nil]; NSArray *arr = [NSArray arrayWithObject:newVC]; [self.pageVC setViewControllers:arr 方向:UIPageViewControllerNavigationDirectionForward 动画:NO 完成:nil]; NSLog(@"%@", self.modelArray);它添加了 VC,但是当我尝试滑动页面时它会崩溃。正如你所说,我需要更新我的数据源中的某些内容,嗯?有任何想法吗? :D 像魅力一样工作!非常感谢您的帮助!

以上是关于点击 UIButton 后如何在 UIPageViewController 中添加新的视图控制器?的主要内容,如果未能解决你的问题,请参考以下文章

UIButton 在第一次点击后不可点击

点击后UIbutton改变颜色[重复]

添加子视图后UIButton无法点击

如何在 Swift 中 UITest 更改 UIButton 的图像?

UIButton 背景图像 以编程方式更改

在使用分配的 IBAction (UIButton) 点击 IBOutlet 后,iOS 应用程序崩溃,没有任何异常