具有自定义代理或数据源的视图控制器的状态保留

tyl*_*mbl 5 delegates objective-c ios uikit-state-preservation

我试图使用iOS 6+(我的应用程序是7.0+)State Preservation来保留从另一个View Controller以模态方式呈现的视图.因此,它具有典型的模态视图控制器解雇模式:

TNTLoginViewController.h包含

@protocol TNTLoginViewControllerDelegate <NSObject>

- (void)TNTLoginViewControllerDismiss:(TNTLoginViewController *)controller;

@end

@interface TNTLoginViewControllerDelegate : NSObject

@interface TNTLoginViewController : UIViewController

@property (weak, nonatomic) IBOutlet id <TNTLoginViewControllerDelegate> delegate;

- (IBAction)getStarted:(id)sender;

@end
Run Code Online (Sandbox Code Playgroud)

getStarted:实现

- (IBAction)getStarted:(id)sender
{
    // Perform login
    ...

    // Dismiss me
    [self.delegate TNTLoginViewControllerDismiss:self];
}
Run Code Online (Sandbox Code Playgroud)

TNTLoginViewControllerDismiss:委托上的方法,它提供了模态

- (void)TNTLoginViewControllerDismiss:(TNTLoginViewController *)controller
{
    [self dismissViewControllerAnimated:YES completion:nil];
}
Run Code Online (Sandbox Code Playgroud)

这一切都像一个魅力!直到国家保护.简单地说,我不知道TNTLoginViewController如何保留其委托.我理解为什么它不能:它只是一个指针!所以我尝试了各种派生委托的方法:

  1. 恢复类:遗憾的是,作为一种类方法,viewControllerWithRestorationIdentifierPath:coder:并没有帮助我指出我具体呈现的View Controller.
  2. 将我的演示VC设置为故事板中的模态VC代理:Xcode不会让我绘制该连接,即使我的演示VC的类公开采用TNTLogingViewControllerDelegate>其标题中的协议.这可能是一个单独的问题,或者可能不允许这样做.
  3. 使用application-delegate-level application:viewControllerWithRestorationIdentifierPath:coder:返回一个模态视图控制器,其委托设置为我呈现的View Controller.我必须能够从App Delegate派生出VC,但它可能有效.

我现在要和#3一起去,但如果有更好的解决方案,有人可以推荐,我会很激动.

设置会产生类似的问题:

  1. 设置数据源,比如表视图.

jhs*_*vik 1

你是对的,这可以从应用程序委托级别做到application:viewControllerWithRestorationIdentifierPath:coder:,但你需要小心/切割你如何做到这一点!

这里的目标是在状态恢复过程中返回一个 TNTLoginViewController ,并将其委托设置为其父级。

首先,您必须创建一个 TNTLoginViewController 对象。你提到了一个故事板,所以我将从那里加载它。我假设您有一个相当标准的 Main.storyboard 文件设置,并且在身份检查器中正确设置了身份。

TNTLoginViewController * loginViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"loginViewController"];
Run Code Online (Sandbox Code Playgroud)

接下来,您需要将其委托设置为父级。我假设有一个 UINavigationController 连接这个模型。要从应用程序委托对象中找到它,您需要深入研究它的 window 属性。

window 属性是一个 UIWindow 对象,它还有另一个名为rootViewController的属性。这是一个 UIViewController 对象。由于我假设有一个 UINavigationController 连接您的模型,因此您需要将此 UIViewController 类型转换为 UINavigationViewController (我会将无法放置在当前信誉级别的链接放置)。

现在,您可以使用导航堆栈顶部的控制器的 topViewController 属性,这就是您想要设置为委托的内容!如果没有,那么您可以导航您想要作为委托的对象的 UINavigationController 对象。

请记住,由于您是从应用程序委托级别设置委托,因此您可能需要在此处指定协议以避免模糊。

在代码中实现最后四个步骤将如下所示。

loginViewController.delegate = (id <TNTLoginViewControllerDelegate>)((UINavigationController *) self.window.rootViewController).topViewController;
Run Code Online (Sandbox Code Playgroud)

然后您可以返回 TNTLoginViewController 并正确设置其委托!

确保不要忘记使用application:viewControllerWithRestorationIdentifierPath:coder:. 您只想在恢复 TNTLoginViewController 的情况下执行此操作。幸运的是,您可以使用传入的identifierComponents参数来检查这一点。将其与身份检查器中的身份名称进行比较,如果不匹配则返回nil。

AppDelegate.m 文件中的最终方法将如下所示。

- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
if ([[identifierComponents lastObject] isEqualToString:@"loginViewController"]) {
    TNTLoginViewController * loginViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"loginViewController"];

    loginViewController.delegate = (id <TNTLoginViewControllerDelegate>)((UINavigationController *) self.window.rootViewController).topViewController;

    return loginViewController;
}

return nil;
}
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助!