【问题标题】:Changing view controller when Segmented Control changes分段控制更改时更改视图控制器
【发布时间】:2014-03-12 06:21:04
【问题描述】:

这个问题快把我逼疯了。当用户更改分段控件的选定“选项卡”时,我正在尝试更改viewController。我花了几个小时进行研究,但无法找到有效的答案或通过情节提要完成的答案。

这真的让我很困扰,因为设置选项卡应用程序非常简单,但是尝试像选项卡应用程序一样使用分段控件是行不通的。我已经知道如何检测在分段控件中选择了哪个索引。我怎样才能做到这一点?

非常感谢。

【问题讨论】:

  • 您是要换出 view 还是 view controller?它们是两种不同的东西。如果是后者,我建议将所有内容整合到一个视图控制器中,并根据需要显示或隐藏子视图。
  • 好吧,我真的是 iOS 新手,我想到的最简单的事情就是像选项卡式应用程序一样更改 viewController。您是否建议制作各种视图并使用相同的视图控制器管理它们?如果是这样,我该怎么做?非常感谢

标签: ios storyboard


【解决方案1】:

注意:答案已更新为 iOS 5+ 的视图控制器包含代码,包括 @interface 部分

在我的一个应用程序中,我在导航栏中有一个带有段控件的视图控制器,单击“选项卡”会切换视图控制器。基本思想是拥有一个视图控制器数组,并使用段索引(和 indexDidChangeForSegmentedControl IBAction)在它们之间切换。

我的应用程序中的示例代码(iOS 5 或更高版本)(这是用于 2 个视图控制器,但它被简单地扩展到多个视图控制器);该代码比 iOS 4 稍长,但会保持对象图完整。此外,它使用 ARC:

@interface MyViewController ()
// Segmented control to switch view controllers
@property (weak, nonatomic) IBOutlet UISegmentedControl *switchViewControllers;

// Array of view controllers to switch between
@property (nonatomic, copy) NSArray *allViewControllers;

// Currently selected view controller
@property (nonatomic, strong) UIViewController *currentViewController;
@end

@implementation UpdateScoreViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    // Create the score view controller
    ViewControllerA *vcA = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerA"];

    // Create the penalty view controller
    ViewControllerB *vcB = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerB"];

    // Add A and B view controllers to the array
    self.allViewControllers = [[NSArray alloc] initWithObjects:vcA, vcB, nil];

    // Ensure a view controller is loaded
    self.switchViewControllers.selectedSegmentIndex = 0;
    [self cycleFromViewController:self.currentViewController toViewController:[self.allViewControllers objectAtIndex:self.switchViewControllers.selectedSegmentIndex]];
}

#pragma mark - View controller switching and saving

- (void)cycleFromViewController:(UIViewController*)oldVC toViewController:(UIViewController*)newVC {

    // Do nothing if we are attempting to swap to the same view controller
    if (newVC == oldVC) return;

    // Check the newVC is non-nil otherwise expect a crash: NSInvalidArgumentException
    if (newVC) {

        // Set the new view controller frame (in this case to be the size of the available screen bounds)
        // Calulate any other frame animations here (e.g. for the oldVC)
        newVC.view.frame = CGRectMake(CGRectGetMinX(self.view.bounds), CGRectGetMinY(self.view.bounds), CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));

        // Check the oldVC is non-nil otherwise expect a crash: NSInvalidArgumentException
        if (oldVC) {

            // Start both the view controller transitions
            [oldVC willMoveToParentViewController:nil];
            [self addChildViewController:newVC];

            // Swap the view controllers
            // No frame animations in this code but these would go in the animations block
            [self transitionFromViewController:oldVC
                              toViewController:newVC
                                      duration:0.25
                                       options:UIViewAnimationOptionLayoutSubviews
                                    animations:^{}
                                    completion:^(BOOL finished) {
                                        // Finish both the view controller transitions
                                        [oldVC removeFromParentViewController];
                                        [newVC didMoveToParentViewController:self];
                                        // Store a reference to the current controller
                                        self.currentViewController = newVC;
                                    }];

        } else {

            // Otherwise we are adding a view controller for the first time
            // Start the view controller transition
            [self addChildViewController:newVC];

            // Add the new view controller view to the ciew hierarchy
            [self.view addSubview:newVC.view];

            // End the view controller transition
            [newVC didMoveToParentViewController:self];

            // Store a reference to the current controller
            self.currentViewController = newVC;
        }
    }
}

- (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl *)sender {

    NSUInteger index = sender.selectedSegmentIndex;

    if (UISegmentedControlNoSegment != index) {
        UIViewController *incomingViewController = [self.allViewControllers objectAtIndex:index];
        [self cycleFromViewController:self.currentViewController toViewController:incomingViewController];
    }

}
@end

原始示例(iOS 4 或更早版本):

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    // Create the score view controller
    AddHandScoreViewController *score = [self.storyboard instantiateViewControllerWithIdentifier:@"AddHandScore"];

    // Create the penalty view controller
    AddHandPenaltyViewController *penalty = [self.storyboard instantiateViewControllerWithIdentifier:@"AddHandPenalty"];

    // Add Score and Penalty view controllers to the array
    self.allViewControllers = [[NSArray alloc] initWithObjects:score, penalty, nil];

    // Ensure the Score controller is loaded
    self.switchViewControllers.selectedSegmentIndex = 0;
    [self switchToController:[self.allViewControllers objectAtIndex:self.switchViewControllers.selectedSegmentIndex]];
}

#pragma mark - View controller switching and saving

- (void)switchToController:(UIViewController *)newVC
{
    if (newVC) {
        // Do nothing if we are in the same controller
        if (newVC == self.currentViewController) return;

        // Remove the current controller if we are loaded and shown
        if([self.currentViewController isViewLoaded]) [self.currentViewController.view removeFromSuperview];

        // Resize the new view controller
        newVC.view.frame = CGRectMake(CGRectGetMinX(self.view.bounds), CGRectGetMinY(self.view.bounds), CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));

        // Add the new controller
        [self.view addSubview:newVC.view];

        // Store a reference to the current controller
        self.currentViewController = newVC;
    }
}

- (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl *)sender {

    NSUInteger index = sender.selectedSegmentIndex;

    if (UISegmentedControlNoSegment != index) {
        UIViewController *incomingViewController = [self.allViewControllers objectAtIndex:index];
        [self switchToController:incomingViewController];
    }

}

【讨论】:

  • 非常好的解决方案。感谢分享。
  • @Robotic Cat:我有点困惑,为什么您将子视图控制器的框架设置为与父视图控制器相同。这个父视图控制器不包含 UISegmentedControl 吗?如果是这样,子视图控制器不会掩盖它吗?
  • @Marplesoft :通常UISegmentedControl 在导航栏中。显然,如果您的 UISegmentedControl 在您的视图控制器中,您应该将您的自动布局约束或框架设置为您的应用程序的正确值。
  • 这是一个很好的例子。当我触摸分段控件(iOS 6,第一个示例)时,我遇到了 EXC_BAD_ADDRESS 崩溃。它有一些基本的感觉,就像我在故事板中错误地构建了一些东西。线索:1)在调试器中,当我触摸控件时,我正确地有一个oldVC和一个newVC,但是一旦完成块运行,就不再有一个oldVC。 'else' 子句有问题吗? 2)当视图第一次加载时,视图中的测试标签没有垂直居中(应该是) - 尽管第二个视图测试标签正确居中。有什么想法吗?谢谢!
  • 考虑到文本布局差异,我关闭了自动布局,分段控件正确切换视图控制器(Xcode 4.6.3)。认为这已解决。
【解决方案2】:

我想说在UIViewController 中更改子视图要简单得多,您可以在情节提要中设置子视图并将它们与控制器中的IBOulets 挂钩您可以设置视图的hidden 属性YES 或 NO 取决于单击的控件。

现在,如果您使用 @Robotic Cat 的方法,这也是一个很好的解决方案,您可以在应用程序的工作方式上更加模块化,考虑到您必须使用我提供的解决方案将所有逻辑放在一个控制器中。

【讨论】:

  • 我喜欢你的建议,只是在 xcode 上试用了一下,看起来不错且易于实现。我唯一担心的是在后台隐藏视图效率低下?
  • 我没有在开发者文档中阅读任何与此相反的内容,但您必须考虑到如果添加大量视觉元素会占用内存,因此请明智地执行此操作。为避免这种情况,您可以即时启动所需的一切,尽管我相信已经设置所有元素的开销很小。例如,我只有 4 个子视图,每个子视图都有 1 或 2 个文本字段。
  • 在我的一个 iPad 应用程序中,我有一个视图控制器,它有 7 个可能显示或隐藏的子视图,每个子视图都有自己的一组控件。总共有大约 60 个视图和子视图(标签、按钮等)。我没有注意到任何缓慢或犹豫,Profier 也没有发现内存或 CPU 出现任何巨大的峰值。显然,您应该彻底测试自己的解决方案,但我怀疑您会遇到问题。
【解决方案3】:

UISegmentedControl 有点不同,它没有委托协议,您必须使用“添加目标”样式。在您的情况下,您要做的是添加一个目标,以便在 UISegmentedControl 更改(可能是父视图控制器)时收到通知,然后该目标可以处理选项卡切换。

例如:

[self.mainSegmentedControl addTarget:self action:@selector(changedSegmentedControl:) forControlEvents:UIControlEventValueChanged];

在此示例中,代码是从可以访问分段控件变量的某个视图/控制器调用的。我们添加自己以调用 changedSegmentedControl: 方法。

然后你会有另一个方法,像这样:

- (void)changedSegmentedControl:(id)sender
{
   UISegmentedControl *ctl = sender;
   NSLog(@"Changed value of segmented control to %d", ctl.selectedSegmentIndex);
   // Code to change View Controller goes here
}

注意:这是从内存中编写的未经测试的代码——请相应地查阅文档。

【讨论】:

    【解决方案4】:

    看看这个 pod:https://github.com/xmartlabs/XLMailBoxContainer。它使视图控制器之间的 UI 动画。这些视图控制器可以扩展 UITableViewController 或任何其他视图控制器。

    希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 2018-07-11
      • 1970-01-01
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多