视图控制器:如何通过编程在视图之间进行切换?

52

简而言之:我想要两个全屏视图,可以在视图A和视图B之间切换。我知道我可以使用Tab Bar控制器,但我不想这样做。我想手动实现它,以便了解其内部运作方式。

我有一个UIViewController作为根控制器:

@interface MyRootController : UIViewController {
    IBOutlet UIView *contentView;
}
@property(nonatomic, retain) UIView *contentView;

@end

contentView连接到一个UIView,我将其作为Nib的子视图添加到“view”中。它是绿色的,我可以全屏看到它,运行良好。

然后,我以基本相同的方式创建了另外两个视图控制器。ViewControllerA具有蓝色背景,ViewControllerB具有黑色背景。只是为了看看哪个处于活动状态。

所以,在myRootController的实现中,我这样做:

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

    ViewControllerA *vcA = [[ViewControllerA alloc] initWithNib];
    [self.contentView addSubview:vcA.view];

    [cvA release];
}

顺便提一下,-initWithNib方法看起来像这样:

- (id)initWithNib { // Load the view nib
    if (self = [super initWithNibName:@"ViewA" bundle:nil]) {
        // do ivar initialization here, if needed
    }
    return self;
}
那可以行。当我启动应用程序时,我可以看到来自ViewControllerA的视图。但现在的重要问题是:视图控制器通常具有所有这些方法,例如:
  • (void)viewWillAppear:(BOOL)animated;
  • (void)viewDidDisappear:(BOOL)animated;
  • (void)viewDidLoad;
如果没有选项卡控制器,那么这些方法将由谁或什么来调用,或者如何调用这些方法“我的”方式?我的意思是:如果我分配了该ViewController的类并且视图变得可见,那么我是否需要注意调用这些方法?它如何知道viewWillAppear、viewDidDisappear或viewDidLoad? 我相信,选项卡控制器在幕后具有所有这些“聪明才智”。或者我错了吗?
更新:我已经测试过了。如果我释放视图控制器(例如:ViewControllerA),我将不会收到viewDidDisappear上的任何日志消息。只有在分配和初始化ViewControllerA时,我会获得一个viewDidLoad。但就是这样。因此,所有迹象都表明UITabBarController非常“聪明”,而我必须找出如何复制它,对吗?
4个回答

50

在《Beginning iPhone Development》的第6章中有一个很好的切换视图的示例。你可以在这里看到它的源代码:http://iphonedevbook.com/

SwitchViewController拥有编程更改视图的代码。


- (IBAction)switchViews:(id)sender
{

    if (self.yellowViewController == nil)
    {
        YellowViewController *yellowController = [[YellowViewController alloc]
                initWithNibName:@"YellowView" bundle:nil];
        self.yellowViewController = yellowController;
        [yellowController release];
    }

    [UIView beginAnimations:@"View Flip" context:nil];
    [UIView setAnimationDuration:1.25];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

    UIViewController *coming = nil;
    UIViewController *going = nil;
    UIViewAnimationTransition transition;

    if (self.blueViewController.view.superview == nil) 
    {   
        coming = blueViewController;
        going = yellowViewController;
        transition = UIViewAnimationTransitionFlipFromLeft;
    }
    else
    {
        coming = yellowViewController;
        going = blueViewController;
        transition = UIViewAnimationTransitionFlipFromRight;
    }

    [UIView setAnimationTransition: transition forView:self.view cache:YES];
    [coming viewWillAppear:YES];
    [going viewWillDisappear:YES];
    [going.view removeFromSuperview];
    [self.view insertSubview: coming.view atIndex:0];
    [going viewDidDisappear:YES];
    [coming viewDidAppear:YES];

    [UIView commitAnimations];

}

13
在引用示例时注明出处是一个好习惯(就像@iPhoney忘记做的那样),这样可以得到加分。 - matm

39

您可以从最简单的removeFromSuperview/insertSubview开始,并逐渐添加代码。


//SwitchViewController.h
#import 
@class BlueViewController;
@class YellowViewController;

@interface SwitchViewController : UIViewController {
    IBOutlet BlueViewController *blueViewController;
    IBOutlet YellowViewController *yellowViewController;
}
- (IBAction)switchViews:(id)sender;
@property (nonatomic, retain) BlueViewController *blueViewController;
@property (nonatomic, retain) YellowViewController *yellowViewController;
@end

//1. remove yellow view and insert blue view
- (IBAction)switchViews:(id)sender {
    if(self.blueViewController.view.superview == nil)
    {
        [yellowViewController.view removeFromSuperview];
        [self.view insertSubview:blueViewController.view atIndex:0];
    }
}

//2. appear=insert, disappear=remove
if(blueViewController.view.superview == nil)
{
    [blueViewController viewWillAppear:YES];
    [yellowViewController viewWillDisappear:YES];

    [yellowViewController.view removeFromSuperview];
    [self.view insertSubview:self.blueViewController.view atIndex:0];

    [yellowViewController viewDidDisappear:YES];
    [blueViewController viewDidAppear:YES];
}

//3. now add animation
[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
//blue view will appear by flipping from right
if(blueViewController.view.superview == nil)
{
    [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight 
                            forView:self.view cache:YES];

    [blueViewController viewWillAppear:YES];
    [yellowViewController viewWillDisappear:YES];

    [yellowViewController.view removeFromSuperview];
    [self.view insertSubview:self.blueViewController.view atIndex:0];

    [yellowViewController viewDidDisappear:YES];
    [blueViewController viewDidAppear:YES];
}
[UIView commitAnimations];


21

如果我理解正确,你要实现的目标非常简单。

只需在你的应用程序代理上添加一个UINavigationController并执行以下操作:

[navigationController pushView:vcA];

委托将相应地被调用:

  • (void)viewWillAppear:(BOOL)animated;
  • (void)viewDidDisappear:(BOOL)animated;
  • (void)viewDidLoad;

当您想要弹出当前视图并推送另一个视图时:

[navigationController popViewControllerAnimated:true];
[navigationController pushView:vcB];

如果你不想显示navigationController,只需使用以下方法:

[navigationBar setHidden:YES];

navigationBar是对应于您的UINavigationController的UINavigationBar。


听起来不错,但我不想要任何导航栏或选项卡 ;) 我只想要裸视图,有一个按钮“转到B”,另一个按钮“转到A”。或者有没有办法不让那个导航栏出现在上面? - Thanks
如果你不想在屏幕上显示导航栏,你可以完全隐藏它。我会更新这个答案,并提供相关信息。 - Pablo Santa Cruz

2
这可能是一个旧问题,但最近我遇到了同样的问题,并且很难找到有效的解决方法。我想在两个互补的视图控制器之间切换,但我希望切换是有动画效果的(内置动画效果可以正常工作),并且如果可能的话,我希望它与故事板兼容。
为了利用内置的过渡效果,UIView的+transitionFromView:toView:duration:options:completion:方法非常好用。但是,它只能在视图之间进行转换,而不能在视图控制器之间转换。
为了让过渡在整个视图控制器之间进行,而不仅仅是在视图之间进行,创建一个自定义的UIStoryboardSegue是正确的方法。无论您是否使用故事板,这种方法都可以让您封装整个过渡,并管理从一个视图控制器传递相关信息到下一个视图控制器。它只涉及子类化UIStoryboardSegue并覆盖单个方法-perform
参考实现请见 RAFlipReplaceSegue,这是我使用这种方法制作的自定义segue。作为额外的奖励,如果旧视图控制器位于 UINavigationController 堆栈中,它还会用新的视图控制器替换旧的视图控制器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接