在 UISplitViewController 和其他视图控制器之间切换的最佳方式?

发布于 2024-10-02 17:34:40 字数 784 浏览 0 评论 0原文

我正在编写一个 iPad 应用程序。应用程序中的屏幕之一非常适合使用 UISplitViewController。但是,应用程序的顶层是主菜单,我不想使用 UISplitViewController。这提出了一个问题,因为 Apple 声明:

  1. UISplitViewController 应该是应用程序中的顶级视图控制器,即它的视图应添加为 UIWindow 的子视图

  2. 如果使用,UISplitViewController 应该在应用程序的生命周期中存在——即不要从 UIWindow 中删除其视图并放置另一个视图,反之亦然

经过阅读和实验,这似乎是满足 Apple 要求的唯一可行选择,而我们自己的要求是使用模式对话框。因此,我们的应用程序在根级别有一个 UISplitViewController (即,它的视图添加为 UIWindow 的子视图),为了显示我们的主菜单,我们将其作为全屏模式对话框推送到 UISplitViewController 上。然后通过关闭主菜单视图控制器模式对话框,我们实际上可以显示我们的分割视图。

这个策略看起来效果很好。但它引出了一个问题:

1)是否有更好的方式来构造这个,没有模态,也满足所有提到的要求?通过将主 UI 作为模式对话框推送来出现似乎有点奇怪。 (模态框应该用于集中的用户任务。)

2)我是否会因为我的方法而面临被应用商店拒绝的风险?根据苹果的人机界面指南,这种模式策略可能是“滥用”模式对话框。但他们还给了我什么其他选择呢?无论如何,他们会知道我正在这样做吗?

I'm authoring an iPad app. One of the screens in the app is perfectly suited to using a UISplitViewController. However, the top level of the app is a main menu, which I don't want to use a UISplitViewController for. This presents a problem, because Apple state that:

  1. UISplitViewController should be the top level view controller in the app, i.e. its view should be added as the subview of UIWindow

  2. if used, UISplitViewController should be there for the lifetime of the app -- i.e. don't remove its view from UIWindow and put another in place, or vice versa

Having read around and experimented, it seems to only viable option to satisfy Apple's requirements and our own is to use modal dialogs. So our app has a UISplitViewController at the root level (i.e. its view added as the subview of UIWindow), and to show our main menu, we push it as a full-screen modal dialog onto the UISplitViewController. Then by dismissing the main menu view controller modal dialog, we can actually show our split view.

This strategy seems to work fine. But it begs the questions:

1) Is there any better way of structuring this, without modals, that also meets all the requirements mentioned? It seems a bit odd having the main UI appear by virtue of being pushed as a modal dialog. (Modals are supposed to be for focused user tasks.)

2) Am I at risk of app store rejection because of my approach? This modal strategy is probably 'misusing' modal dialogs, as per Apple's human interface guidelines. But what other choice have they given me? Would they know that I'm doing this, anyway?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

初雪 2024-10-09 17:34:40

我真的不相信在 UISplitViewController (例如登录表单)之前显示一些 UIViewController 的概念会如此复杂,直到我不得不创建这种视图层次结构.

我的示例基于 iOS 8 和 XCode 6.0 (Swift),所以我不确定这个问题以前是否以同样的方式存在,或者是由于 iOS 8 引入的一些新错误,但从我发现的所有类似问题来看,我没有看到这个问题的完整“不是很老套”的解决方案。

我将引导您完成我在结束之前尝试过的一些事情并提供解决方案(在本文末尾)。每个示例都基于在未启用 CoreData 的情况下从 Master-Detail 模板创建新项目。


第一次尝试(模态转场到 UISplitViewController):

  1. 创建新的 UIViewController 子类(例如 LoginViewController)
  2. 在 Storyboard 中添加新的视图控制器,将其设置为初始值视图控制器(而不是 UISplitViewController)并将其连接到 LoginViewController
  3. 将 UIButton 添加到 LoginViewController 并创建从该按钮到 UISplitViewController 的模式转
  4. 场 将 UISplitViewController 的样板设置代码从 AppDelegate 的 didFinishLaunchingWithOptions 移动到 LoginViewController 的 prepareForSegue

这几乎成功了。我说几乎,因为在使用 LoginViewController 启动应用程序并点击按钮并转到 UISplitViewController 后,会出现一个奇怪的错误:在方向更改时显示和隐藏主视图控制器不再是动画的。

经过一段时间的努力解决这个问题并且没有真正的解决方案,我认为它在某种程度上与UISplitViewController必须是rootViewController的奇怪的规则有关(在这种情况下不是,LoginViewController是)所以我给出了从这个不太完美的解决方案开始。


第二次尝试(来自UISplitViewController的模态segue):

  1. 创建新的UIViewController子类(例如LoginViewController)
  2. 在故事板中添加新的视图控制器,并将其连接到LoginViewController(但这次将UISplitViewController保留为初始视图控制器)
  3. 创建从UISplitViewController到LoginViewController的模态segue
  4. 添加UIButton 到 LoginViewController 并从该按钮创建 unwind segue

最后,在用于设置 UISplitViewController 的样板代码之后将此代码添加到 AppDelegate 的 didFinishLaunchingWithOptions 中:

window?.makeKeyAndVisible()
splitViewController.performSegueWithIdentifier("segueToLogin", sender: self)
return true

或者尝试使用此代码:

window?.makeKeyAndVisible()
let loginViewController = splitViewController.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
splitViewController.presentViewController(loginViewController, animated: false, completion: nil)
return true

这两个示例都会产生相同的几个不好的结果:

  1. 控制台输出:的开始/结束外观转换的不平衡调用
  2. UISplitViewController 必须在 LoginViewController 模态连接之前首先显示(我宁愿只显示登录表单,这样用户就不会登录之前请参阅 UISplitViewController)
  3. Unwind segue 不会被调用(这完全是另一个错误,我现在不讨论这个故事)

解决方案(更新 rootViewController)

我发现正确工作的唯一方法是更改​​窗口的 rootViewController动态:

  1. 为 LoginViewController 和 UISplitViewController 定义 Storyboard ID,
    并向AppDelegate添加某种loggedIn属性。
  2. 基于此属性,实例化适当的视图控制器,然后将其设置为 rootViewController。
  3. didFinishLaunchingWithOptions 中执行此操作时不使用动画,但从 UI 调用时会显示动画。

这是来自 AppDelegate 的示例代码:

var loggedIn = false

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    setupRootViewController(false)
    return true
}

func setupRootViewController(animated: Bool) {
    if let window = self.window {
        var newRootViewController: UIViewController? = nil
        var transition: UIViewAnimationOptions

        // create and setup appropriate rootViewController
        if !loggedIn {
            let loginViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
            newRootViewController = loginViewController
            transition = .TransitionFlipFromLeft

        } else {
            let splitViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("SplitVC") as UISplitViewController
            let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
            navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
            splitViewController.delegate = self

            let masterNavigationController = splitViewController.viewControllers[0] as UINavigationController
            let controller = masterNavigationController.topViewController as MasterViewController

            newRootViewController = splitViewController
            transition = .TransitionFlipFromRight
        }

        // update app's rootViewController
        if let rootVC = newRootViewController {
            if animated {
                UIView.transitionWithView(window, duration: 0.5, options: transition, animations: { () -> Void in
                    window.rootViewController = rootVC
                    }, completion: nil)
            } else {
                window.rootViewController = rootVC
            }
        }
    }
}

这是来自 LoginViewController 的示例代码:

@IBAction func login(sender: UIButton) {
    let delegate = UIApplication.sharedApplication().delegate as AppDelegate
    delegate.loggedIn = true
    delegate.setupRootViewController(true)
}

我还想听听是否有一些更好/更干净的方法可以使其在 iOS 8 中正常工作。

I seriously didn't believe that this concept of having some UIViewController to show before UISplitViewController (login form for example) turns out to be so complicated, until I had to create that kind of view hiearchy.

My example is based on iOS 8 and XCode 6.0 (Swift), so I'm not sure if this problem existed before in a same way, or it's due to some new bugs introduced with iOS 8, but from all of the similar questions I found, I didn't see complete 'not very hacky' solution to this problem.

I'll guide you through some of the things I have tried before I ended up with a solution (at the end of this post). Each example is based on creating new project from Master-Detail template without CoreData enabled.


First try (modal segue to UISplitViewController):

  1. create new UIViewController subclass (LoginViewController for example)
  2. add new view controller in storyboard, set it as initial view controller (instead of UISplitViewController) and connect it to LoginViewController
  3. add UIButton to LoginViewController and create modal segue from that button to UISplitViewController
  4. move boilerplate setup code for UISplitViewController from AppDelegate's didFinishLaunchingWithOptions to LoginViewController's prepareForSegue

This almost worked. I say almost, because after the app is started with LoginViewController and you tap button and segue to UISplitViewController, there is a strange bug going on: showing and hiding master view controller on orientation change is no longer animated.

After some time struggling with this problem and without real solution, I thought that it's somehow connected with that weird rule that UISplitViewController must be rootViewController (and in this case it isn't, LoginViewController is) so I gave up from this not so perfect solution.


Second try (modal segue from UISplitViewController):

  1. create new UIViewController subclass (LoginViewController for example)
  2. add new view controller in storyboard, and connect it to LoginViewController (but this time leave UISplitViewController to be initial view controller)
  3. create modal segue from UISplitViewController to LoginViewController
  4. add UIButton to LoginViewController and create unwind segue from that button

Finally, add this code to AppDelegate's didFinishLaunchingWithOptions after boilerplate code for setting up UISplitViewController:

window?.makeKeyAndVisible()
splitViewController.performSegueWithIdentifier("segueToLogin", sender: self)
return true

or try with this code instead:

window?.makeKeyAndVisible()
let loginViewController = splitViewController.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
splitViewController.presentViewController(loginViewController, animated: false, completion: nil)
return true

Both of these examples produce same several bad things:

  1. console outputs: Unbalanced calls to begin/end appearance transitions for <UISplitViewController: 0x7fc8e872fc00>
  2. UISplitViewController must be shown first before LoginViewController is segued modally (I would rather present only the login form so the user doesn't see UISplitViewController before logged in)
  3. Unwind segue doesn't get called (this is totally other bug, and I'm not going into that story now)

Solution (update rootViewController)

The only way I found which works properly is if you change window's rootViewController on the fly:

  1. Define Storyboard ID for LoginViewController and UISplitViewController,
    and add some kind of loggedIn property to AppDelegate.
  2. Based on this property, instantiate appropriate view controller and after that set it as rootViewController.
  3. Do it without animation in didFinishLaunchingWithOptions but animated when called from the UI.

Here is sample code from AppDelegate:

var loggedIn = false

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    setupRootViewController(false)
    return true
}

func setupRootViewController(animated: Bool) {
    if let window = self.window {
        var newRootViewController: UIViewController? = nil
        var transition: UIViewAnimationOptions

        // create and setup appropriate rootViewController
        if !loggedIn {
            let loginViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
            newRootViewController = loginViewController
            transition = .TransitionFlipFromLeft

        } else {
            let splitViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("SplitVC") as UISplitViewController
            let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
            navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
            splitViewController.delegate = self

            let masterNavigationController = splitViewController.viewControllers[0] as UINavigationController
            let controller = masterNavigationController.topViewController as MasterViewController

            newRootViewController = splitViewController
            transition = .TransitionFlipFromRight
        }

        // update app's rootViewController
        if let rootVC = newRootViewController {
            if animated {
                UIView.transitionWithView(window, duration: 0.5, options: transition, animations: { () -> Void in
                    window.rootViewController = rootVC
                    }, completion: nil)
            } else {
                window.rootViewController = rootVC
            }
        }
    }
}

And this is sample code from LoginViewController:

@IBAction func login(sender: UIButton) {
    let delegate = UIApplication.sharedApplication().delegate as AppDelegate
    delegate.loggedIn = true
    delegate.setupRootViewController(true)
}

I would also like to hear if there is some better/cleaner way for this to work properly in iOS 8.

世界等同你 2024-10-09 17:34:40

触动!遇到了同样的问题并使用模态以相同的方式解决了它。就我而言,它是一个登录视图,然后主菜单也会在分割视图之前显示。我使用了与你所想的相同的策略。我(以及我采访过的其他几位知识渊博的 iOS 人员)找不到更好的出路。对我来说效果很好。无论如何,用户永远不会注意到模式。如此呈现它们。是的,我还可以告诉你,应用程序商店中有很多应用程序在幕后做着同样的事情。 :) 另一方面,如果您某个时候找到更好的出路,请告诉我:)

Touche! Ran in to the same issue and solved it the same way using modals. In my case it was a login view and then the main menu as well to be shown before the splitview. I used the same strategy as thought out by you. I (as well as several other knowledgeable iOS folks I spoke to) could not find a better way out. Works fine for me. User never notices the modal anyway. Present them so. And yes I can also tell you that there are quite a few apps doing the same under the hood tricks on the App store. :) On another note, do let me know if you figure a better way out somehow someway sometime :)

回首观望 2024-10-09 17:34:40

谁说只能有一个窗口? :)

看看我的回答关于这个类似问题是否有帮助。

这种方法对我来说非常有效。只要您不必担心多个显示或状态恢复,这个链接的代码应该足以完成您需要的操作:您不必让您的逻辑向后看或重写现有代码,并且仍然可以利用在您的应用程序中更深入地了解 UISplitView - 不会(据我所知)违反 Apple 准则。

And who said you can have only one window ? :)

See if my answer on this similar question can help.

This approach is working very well for me. As long as you don't have to worry about multiple displays or state restoration, this linked code should be enough to do what you need: you don't have to make your logic look backwards or rewrite existing code, and can still take advantage of the UISplitView in a deeper level within your application - without (AFAIK) breaking Apple guidelines.

九公里浅绿 2024-10-09 17:34:40

对于未来遇到同样问题的 iOS 开发人员:这里有另一个答案和解释。您必须将其设为根视图控制器。如果不是,则覆盖模态。

UISplitviewcontroller 不作为根视图控制器

For future iOS developers running into the same issue: here's another answer and explanations. You HAVE to make it root view controller. If it is not, overlay a modal.

UISplitviewcontroller not as a rootview controller

长不大的小祸害 2024-10-09 17:34:40

刚刚在一个项目中遇到这个问题,我想分享一下我的解决方案。在我们的例子中(针对 iPad),我们希望从两个视图控制器可见的 UISplitViewController 开始(使用 preferredDisplayMode = .allVisible)。在细节(右)层次结构中的某个时刻(我们这一侧也有一个导航控制器),我们希望在整个分割视图控制器上推送一个新的视图控制器(不使用模式转换)。

在 iPhone 上,这种行为是免费的——因为任何时候只有一个视图控制器可见。但在 iPad 上,我们必须想出其他办法。我们最终选择了一个根容器视图控制器,它将分割视图控制器作为子视图控制器添加到其中。该根视图控制器嵌入在导航控制器中。当分割视图控制器中的详细视图控制器想要在整个分割视图控制器上推送一个新的控制器时,根视图控制器会用它的导航控制器推送这个新的视图控制器。

Just ran into this problem on a project and thought I'd share my solution. In our case (for iPad), we wanted to start with a UISplitViewController with both view controllers visible (using preferredDisplayMode = .allVisible). At some point in the detail (right) hierarchy (we had a navigation controller for this side, too) we wanted to push a new view controller over the entire split view controller (not use a modal transition).

On iPhone, this behavior comes for free—as only one view controller is visible at any time. But on iPad we had to figure something else out. We ended up going with a root container view controller that adds the split view controller to it as a child view controller. This root view controller is embedded in a navigation controller. When the detail view controller in the split view controller wants to push a new controller over the entire split view controller, the root view controller pushes this new view controller with its navigation controller.

铜锣湾横着走 2024-10-09 17:34:40

我想贡献我的方法来呈现 UISplitViewController,正如您可能希望通过 -presentViewController:animated:completion: (我们都知道这行不通)。
我创建了一个 UISplitViewController 子类,它响应:

-presentAsRootViewController
-returnToPreviousViewController

该类与其他成功的方法一样,将 UISplitViewController 设置为窗口的 rootViewController,但使用类似于您通过 -presentViewController:animated:completion 获得的动画(默认情况下)类似的动画:

PresentableSplitViewController.h

#import <UIKit/UIKit.h>    
@interface PresentableSplitViewController : UISplitViewController    
- (void) presentAsRootViewController;
@end

PresentableSplitViewController.m

#import "PresentableSplitViewController.h"

@interface PresentableSplitViewController ()
@property (nonatomic, strong) UIViewController *previousViewController;
@end

@implementation PresentableSplitViewController

- (void) presentAsRootViewController {

    UIWindow *window=[[[UIApplication sharedApplication] delegate] window];
    _previousViewController=window.rootViewController;

    UIView *windowSnapShot = [window snapshotViewAfterScreenUpdates:YES];
    window.rootViewController = self;

    [window insertSubview:windowSnapShot atIndex:0];

    CGRect dstFrame=self.view.frame;

    CGSize offset=CGSizeApplyAffineTransform(CGSizeMake(0, 1), window.rootViewController.view.transform);
    offset.width*=self.view.frame.size.width;
    offset.height*=self.view.frame.size.height;
    self.view.frame=CGRectOffset(self.view.frame, offset.width, offset.height);

    [UIView animateWithDuration:0.5
                          delay:0.0
         usingSpringWithDamping:1.0
          initialSpringVelocity:0.0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{
                         self.view.frame=dstFrame;
                     } completion:^(BOOL finished) {
                         [windowSnapShot removeFromSuperview];
                     }];
}

- (void) returnToPreviousViewController {
    if(_previousViewController) {

        UIWindow *window=[[[UIApplication sharedApplication] delegate] window];

        UIView *windowSnapShot = [window snapshotViewAfterScreenUpdates:YES];
        window.rootViewController = _previousViewController;

        [window addSubview:windowSnapShot];

        CGSize offset=CGSizeApplyAffineTransform(CGSizeMake(0, 1), window.rootViewController.view.transform);
        offset.width*=windowSnapShot.frame.size.width;
        offset.height*=windowSnapShot.frame.size.height;

        CGRect dstFrame=CGRectOffset(windowSnapShot.frame, offset.width, offset.height);

        [UIView animateWithDuration:0.5
                              delay:0.0
             usingSpringWithDamping:1.0
              initialSpringVelocity:0.0
                            options:UIViewAnimationOptionCurveEaseInOut
                         animations:^{
                             windowSnapShot.frame=dstFrame;
                         } completion:^(BOOL finished) {
                             [windowSnapShot removeFromSuperview];
                             _previousViewController=nil;
                         }];
    }
}

@end

I'd like to contribute my approach to presenting a UISplitViewController, as you might like to via -presentViewController:animated:completion: (we all know that won't work though).
I created a UISplitViewController subclass which responds to:

-presentAsRootViewController
-returnToPreviousViewController

The class, which like other successful approaches, sets the UISplitViewController as the window's rootViewController but does so with an animation similar to what you get (by default) with -presentViewController:animated:completion:

PresentableSplitViewController.h

#import <UIKit/UIKit.h>    
@interface PresentableSplitViewController : UISplitViewController    
- (void) presentAsRootViewController;
@end

PresentableSplitViewController.m

#import "PresentableSplitViewController.h"

@interface PresentableSplitViewController ()
@property (nonatomic, strong) UIViewController *previousViewController;
@end

@implementation PresentableSplitViewController

- (void) presentAsRootViewController {

    UIWindow *window=[[[UIApplication sharedApplication] delegate] window];
    _previousViewController=window.rootViewController;

    UIView *windowSnapShot = [window snapshotViewAfterScreenUpdates:YES];
    window.rootViewController = self;

    [window insertSubview:windowSnapShot atIndex:0];

    CGRect dstFrame=self.view.frame;

    CGSize offset=CGSizeApplyAffineTransform(CGSizeMake(0, 1), window.rootViewController.view.transform);
    offset.width*=self.view.frame.size.width;
    offset.height*=self.view.frame.size.height;
    self.view.frame=CGRectOffset(self.view.frame, offset.width, offset.height);

    [UIView animateWithDuration:0.5
                          delay:0.0
         usingSpringWithDamping:1.0
          initialSpringVelocity:0.0
                        options:UIViewAnimationOptionCurveEaseInOut
                     animations:^{
                         self.view.frame=dstFrame;
                     } completion:^(BOOL finished) {
                         [windowSnapShot removeFromSuperview];
                     }];
}

- (void) returnToPreviousViewController {
    if(_previousViewController) {

        UIWindow *window=[[[UIApplication sharedApplication] delegate] window];

        UIView *windowSnapShot = [window snapshotViewAfterScreenUpdates:YES];
        window.rootViewController = _previousViewController;

        [window addSubview:windowSnapShot];

        CGSize offset=CGSizeApplyAffineTransform(CGSizeMake(0, 1), window.rootViewController.view.transform);
        offset.width*=windowSnapShot.frame.size.width;
        offset.height*=windowSnapShot.frame.size.height;

        CGRect dstFrame=CGRectOffset(windowSnapShot.frame, offset.width, offset.height);

        [UIView animateWithDuration:0.5
                              delay:0.0
             usingSpringWithDamping:1.0
              initialSpringVelocity:0.0
                            options:UIViewAnimationOptionCurveEaseInOut
                         animations:^{
                             windowSnapShot.frame=dstFrame;
                         } completion:^(BOOL finished) {
                             [windowSnapShot removeFromSuperview];
                             _previousViewController=nil;
                         }];
    }
}

@end
酒废 2024-10-09 17:34:40

我做了一个 UISplitView 作为初始视图,然后它模态地变为全屏 UIView,然后返回 UISplitView。如果您需要返回 SplitView,则必须使用自定义 Segue。

阅读此链接(从日语翻译)

UIViewController 到 UISplitViewController

I did a UISplitView as initial view, than it goes modally to a fullscreen UIView and back to UISplitView. If you need to go back to the SplitView you have to use a custom segue.

Read this link (translate it from japanese)

UIViewController to UISplitViewController

携余温的黄昏 2024-10-09 17:34:40

添加@tadija的答案,我遇到了类似的情况:

我的应用程序仅适用于手机,我正在添加平板电脑用户界面。我决定在同一个应用程序中使用 Swift 进行操作,并最终迁移所有应用程序以使用相同的故事板(当我觉得 iPad 版本稳定时,使用 XCode6 中的新类将其用于手机应该是微不足道的)。

我的场景中尚未定义任何转场,但它仍然有效。

我的应用程序委托中的代码是用 ObjectiveC 编写的,略有不同 - 但使用相同的想法。
请注意,与前面的示例不同,我使用的是场景中的默认视图控制器。我觉得这也适用于 IOS7/IPhone,其中运行时将生成常规的 UINavigationController 而不是 UISplitViewController。我什至可能添加新代码,将登录视图控制器推送到 iPhone 上,而不是更改 rootVC。

- (void) setupRootViewController:(BOOL) animated {
    UIViewController *newController = nil;
    UIStoryboard *board = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    UIViewAnimationOptions transition = UIViewAnimationOptionTransitionCrossDissolve;

    if (!loggedIn) {
        newController = [board instantiateViewControllerWithIdentifier:@"LoginViewController"];
    } else {
        newController = [board instantiateInitialViewController];
    }

    if (animated) {
        [UIView transitionWithView: self.window duration:0.5 options:transition animations:^{
            self.window.rootViewController = newController;
            NSLog(@"setup root view controller animated");
        } completion:^(BOOL finished) {
            NSLog(@"setup root view controller finished");
        }];
    } else {
        self.window.rootViewController = newController;
    }
}

Adding to the answer of @tadija I am in a similar situation:

My app was for phones only, and I am adding a tablet UI. I decided doing it in Swift in the same app - and eventually migrate all the app to use the same storyboard (when I feel the IPad version is stable, using it for phones should be trivial with the new classes from XCode6).

No segues were defined in my scene yet and it still works.

My the code in my app delegate is in ObjectiveC, and is slightly different - but uses the same idea.
Note that I am using the default view controller from the scene, unlike previous examples. I feel this will also work on IOS7/IPhone in which the runtime will generate a regular UINavigationController instead of a UISplitViewController. I might even add new code which will push the login view controller on IPhones, instead of changing the rootVC.

- (void) setupRootViewController:(BOOL) animated {
    UIViewController *newController = nil;
    UIStoryboard *board = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    UIViewAnimationOptions transition = UIViewAnimationOptionTransitionCrossDissolve;

    if (!loggedIn) {
        newController = [board instantiateViewControllerWithIdentifier:@"LoginViewController"];
    } else {
        newController = [board instantiateInitialViewController];
    }

    if (animated) {
        [UIView transitionWithView: self.window duration:0.5 options:transition animations:^{
            self.window.rootViewController = newController;
            NSLog(@"setup root view controller animated");
        } completion:^(BOOL finished) {
            NSLog(@"setup root view controller finished");
        }];
    } else {
        self.window.rootViewController = newController;
    }
}
一影成城 2024-10-09 17:34:40

另一个选择:在详细信息视图控制器中,我显示一个模态视图控制器:

let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
if (!appDelegate.loggedIn) {
    // display the login form
    let storyboard = UIStoryboard(name: "Storyboard", bundle: nil)
    let login = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
    self.presentViewController(login, animated: false, completion: { () -> Void in
       // user logged in and is valid now
       self.updateDisplay()
    })
} else {
    updateDisplay()
}

在未设置登录标志的情况下,不要关闭登录控制器。请注意,在 iPhone 中,主视图控制器将首先出现,因此主视图控制器上需要有一个非常相似的代码。

Another option: In the details view controller I display a modal view controller:

let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
if (!appDelegate.loggedIn) {
    // display the login form
    let storyboard = UIStoryboard(name: "Storyboard", bundle: nil)
    let login = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
    self.presentViewController(login, animated: false, completion: { () -> Void in
       // user logged in and is valid now
       self.updateDisplay()
    })
} else {
    updateDisplay()
}

Don't dismiss the login controller without setting the login flag. Note that in IPhones the master view controller will come first, so a very similar code will need to be on the master view controller.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文