以编程方式创建转场动画

207

我有一个普通的UIViewController,所有我的UIViewsControllers都会继承它以重用一些共同操作。

我想在这个“Common”UIViewController上设置一个segue,以便所有其他UIViewControllers都可以继承它。

我正在尝试找出如何以编程方式实现这一点。

我猜问题也可以是如何为所有UIViewControllers设置一个segue,而不用手动进入story board进行设置。

13个回答

347

我想我可以提供另一种可能性。你可以使用一个不附加到操作的segue将storyboard中的两个场景连接起来,然后在你的视图控制器中以编程方式触发segue。具体做法是,在分界面场景底部的文件所有者图标处拖动,并右键拖动到目标场景。我会放一张图片来帮助说明。

enter image description here

会弹出“手动跳转”的选项。我选择了Push类型。点击小方块并确保你在属性检查器中。给它一个标识符,在代码中引用它。

enter image description here

好的,接下来我将使用编程方式的工具栏按钮进行Segue。 在viewDidLoad或其他地方,我将使用以下代码在导航栏上创建一个按钮项:

UIBarButtonItem *buttonizeButton = [[UIBarButtonItem alloc] initWithTitle:@"Buttonize"
                                                                    style:UIBarButtonItemStyleDone
                                                                   target:self
                                                                   action:@selector(buttonizeButtonTap:)];
self.navigationItem.rightBarButtonItems = @[buttonizeButton];

注意选择器是buttonizeButtonTap:。因此,为该按钮编写一个无返回值的方法,并在该方法中调用segue,如下所示:

好的,请注意选择器是 buttonizeButtonTap:。因此,您需要为该按钮编写一个 void 方法,在该方法内部调用 segue,代码如下:

-(void)buttonizeButtonTap:(id)sender{
    [self performSegueWithIdentifier:@"Associate" sender:sender];
    }

当调用prepareForSegue时,sender参数是必需的,以标识按钮。prepareForSegue是框架方法,在该方法中您将实例化场景并传递它需要执行其工作所需的任何值。这是我的方法示例:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"Associate"])
    {
        TranslationQuizAssociateVC *translationQuizAssociateVC = [segue destinationViewController];
        translationQuizAssociateVC.nodeID = self.nodeID; //--pass nodeID from ViewNodeViewController
        translationQuizAssociateVC.contentID = self.contentID;
        translationQuizAssociateVC.index = self.index;
        translationQuizAssociateVC.content = self.content;
    }
}

我进行了测试,它可以正常工作。


@MichaelRowe 这个方法如何消除需要使用segues的必要性?我认为你仍然需要在Storyboard上拖放到目标控制器。 - aherrick
@cocoanut 我遇到了一个错误,提示“应用程序试图以模态方式呈现一个活动控制器”,请问有什么帮助吗? - Bala
1
手动Segue“Push”已被弃用,请使用“Show”。此答案有更多细节。@smileBot请更新答案。 - NAbbas
这对我有用,但我不得不使用“Show”而不是“Push”,并且我没有使用此答案中的最后一种方法,它仍然有效。非常感谢+1!!! - Lazar Kukolj
也许我没有理解问题,这意味着我不理解答案...我的目标是更改UIStoryboardSegue的源,以便我可以从一个已呈现的视图控制器推送它。如果我想要这样做,NIB似乎是正确的解决方案。 - below
显示剩余2条评论

170
根据定义,segue实际上不能独立于storyboard存在。这甚至反映在类的名称中:UIStoryboardSegue。你无法以编程方式创建segue - 是故事板运行时为您创建它们。通常情况下,您可以在视图控制器的代码中调用performSegueWithIdentifier:,但这需要有一个已经设置好引用的segue存在于storyboard中。
不过,我认为您问的是如何在通用视图控制器(基类)中创建一个方法,该方法将转换到新的视图控制器,并将被所有派生类继承。您可以通过向基类视图控制器添加此方法来实现此目的:
- (IBAction)pushMyNewViewController
{
    MyNewViewController *myNewVC = [[MyNewViewController alloc] init];

    // do any setup you need for myNewVC

    [self presentModalViewController:myNewVC animated:YES];
}

然后在您的派生类中,在适当的按钮被点击或表格行被选中时调用该方法。


4
谢谢。可惜我们不能用程序来完成它。这将真正提高源代码的质量(减少重复总是好的)。我会尝试您的建议。 - Tiago Veloso
2
@jonkroll,在switch语句中是否可以根据索引调用/执行segue? - codejunkie
5
可以这样做。您需要使用名为 performSegueWithIdentifier:sender:UIViewController 方法来实现。 - jonkroll
2
我一直在以编程方式创建和执行segue(请参见我的答案)。如果您的答案是正确的,那么我的代码有什么问题吗? - Jean-Philippe Pellet
15
iOS 6及以上版本更新:UIView的presentModalViewController:animated:已被弃用。从文档中可以看到,*(在iOS 6.0中已被弃用,请改用presentViewController:animated:completion:代替。)* - user
显示剩余4条评论

82

我一直在使用这段代码来实例化我的自定义segue子类并以编程方式运行它。它似乎起作用了。这样做有什么问题吗?我感到困惑,因为其他所有答案都说这样做不可能。

UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"OtherViewControllerId"];
MyCustomSegue *segue = [[MyCustomSegue alloc] initWithIdentifier:@"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];

3
这是一个UIStoryboardSegue的定制子类。 - Jean-Philippe Pellet
7
很多人(包括我)避免使用故事板。它们很难合并,而且没有编译时检查,以确保我传递给performSegueWithIdentifier:的ID确实在故事板中定义。如果我自己创建转场,就可以避免所有问题。 - Jean-Philippe Pellet
2
非常感谢Jean-Philippe!我有很多页面都需要通过自定义segue动画退出到主菜单。在Storyboard上创建所有的链接是荒谬的。这段代码非常有用,谢谢。 - Custom Bonbons
3
我同意Jean-Philippe的看法,管理故事板很麻烦。当然,点击几个视图并添加一些segue很容易,但是当有三个开发人员同时操作时,管理定义了16个segue的6个视图的XML就很可怕了。总之,重点是:代码可以让你掌控一切,而Xcode生成的XML却无法做到这一点。 - Krystian
3
我在iOS7中看到了[segue perform]的崩溃,不确定其他人是否也遇到了这个问题。 - Eric Chen
显示剩余6条评论

44

看起来这个问题已经得到回答并被接受,但我想补充一些细节。

为了解决一个问题,在该问题中,我会首先呈现登录视图作为第一个屏幕,然后如果登录正确,希望跳转到应用程序。 我从登录视图控制器创建了segue到根视图控制器,并赋予了一个标识符,比如“myidentifier”。

然后,在检查所有登录代码是否正确的情况下,如果登录正确,我会调用

[self performSegueWithIdentifier: @"myidentifier" sender: self];

我最大的误解是尝试将segue放在按钮上,并在找到segue后中断它。


4
我曾在另一条评论中写道:我一直在以编程方式创建和执行自定义的segue(请参见我的答案)。 - Jean-Philippe Pellet

32

你需要将代码连接到你正在使用的 UIStoryboard 上。确保进入你的 UIStoryboard 中的 YourViewController,单击其周围的边框,然后设置其 identifier 字段为一个 NSString,在代码中调用它。

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
YourViewController *yourViewController = 
 (YourViewController *)
  [storyboard instantiateViewControllerWithIdentifier:@"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];

1
我理解了,但是如果我想呈现的viewController在storyboard中嵌入了NavigationController怎么办?从我找到的资料中,我可以初始化一个NavigationController来嵌入它,但是在storyboard中,我已经设置了推送segue来呈现需要呈现的视图。 - jhilgert00
1
你能详细说明一下吗?我认为这就是我的问题所在,但似乎找不到如何/在哪里进行操作... - jesses.co.tt
1
即使这个解决方案是正确的,它是关于避免任何segue,但问题是关于segue的。通过这种方式,您可以在storyboards中连接或转换两个场景而不需要segue。 - BootMaker

16

对于在故事板中的控制器。

jhilgert00这就是你要找的吗?

-(IBAction)nav_goHome:(id)sender {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:@"HomeController"];
[self.navigationController pushViewController: myController animated:YES];

}

或者...

[self performSegueWithIdentifier:@"loginMainSegue" sender:self];

3

故事板Segue不能在故事板之外创建。尽管存在缺点,您仍需要将其连接起来。

UIStoryboardSegue参考清楚地说明:

您不会直接创建segue对象。相反,当运行时必须在两个视图控制器之间执行segue时,故事板运行时会创建它们。如果您想要的话,仍然可以使用UIViewController的performSegueWithIdentifier:sender:方法以编程方式启动segue。您可能会这样做,以从以编程方式添加并因此在Interface Builder中不可用的源启动segue。

您仍然可以使用presentModalViewController:pushViewController:animated:调用以编程方式告诉故事板呈现视图控制器,但是您需要一个故事板实例。

您可以调用UIStoryboard的类方法,以获取具有主束的nil的命名故事板。

storyboardWithName:bundle:


3
我想澄清一下...
一个常见的误解,事实上我也有一段时间认为是这样的,就是故事板的segue是由prepareForSegue:sender:方法触发的。但事实并非如此。无论您是否为(出发的)视图控制器实现了prepareForSegue:sender:方法,故事板的segue都会执行。
我从Paul Hegarty的优秀的iTunesU讲座中学到了这一点。很抱歉,我不记得是哪个讲座了。
如果您在故事板中在两个视图控制器之间连接了segue,但没有实现prepareForSegue:sender:方法,则segue仍将转换到目标视图控制器。但它将未经准备地转换到该视图控制器。
希望这可以帮助到你。

3

2

首先,假设您在故事板中有两个不同的视图,并且您希望从一个屏幕导航到另一个屏幕,请按照以下步骤操作:

1). 使用类文件和身份检查器中的故事板ID定义所有视图。

2). 确保将导航控制器添加到第一个视图中。在Storyboard中选择它,然后选择Editor > Embed In > Navigation Controller

3). 在第一个类中,导入“secondClass.h”

#import "ViewController.h
#import "secondController.h"

4). 在执行segue的IBAction中添加此命令

secondController *next=[self.storyboard instantiateViewControllerWithIdentifier:@"second"];
[self.navigationController pushViewController:next animated:YES];

5). @"second" 是指 secondview controller 类和 storyboard id。


self.storyboard should be: UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; - masipcat
@masipcat和故事板名称可能取决于您如何设置Xcode项目,在我的项目中它是“Main.storyboard”,因此我使用了storyboardWithName:@“Main” - ammianus
@sanket-chauhan 如果你的第一个控制器没有嵌入导航控制器,你也可以使用 [self showDetailViewController:next sender:self];[self showViewController:next sender:self]; 来显示下一个视图。 - ammianus

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