10

我正在尝试在使用 iOS 6+ 和情节提要的应用程序中实现状态恢复,但我在寻找防止重复调用繁重方法的方法时遇到问题。

如果我只是启动应用程序,那么我需要在以下位置设置 UI viewDidLoad

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
}

这在正常的、非状态恢复的世界中运行良好。现在我添加了状态恢复,在恢复一些属性后,我需要使用这些属性更新 UI:

- (void)decodeRestorableStateWithCoder:(NSCoder *)coder {
    [super decodeRestorableStateWithCoder:coder];
    // restore properties and stuff
    // [...]
    [self setupUI];
}

所以现在发生的情况是首先setupUI调用方法 from viewDidLoad,然后再次调用 from decodeRestorableStateWithCoder:。我没有看到我可以覆盖总是最后调用的方法。

这是方法调用的正常顺序:

  • 从笔尖醒来
  • viewDidLoad
  • 视图将出现
  • viewDidAppear

使用状态恢复时,这称为:

  • 从笔尖醒来
  • viewDidLoad
  • decodeRestorableStateWithCoder
  • 视图将出现
  • viewDidAppear

我无法将调用setupUI放入,viewWillAppear因为这样每次您本地返回视图时也会执行它。

decodeRestorableStateWithCoder如果在之前调用它会更方便,viewDidLoad因为这样你就可以使用恢复的属性。viewDidLoad可悲的是,情况并非如此,所以......当我知道我需要马上重新做一遍时,我怎么能阻止做这项工作decodeRestorableStateWithCoder

4

8 回答 8

6

如果您正在以编程方式进行状态恢复(即不使用情节提要),您可以使用+ viewControllerWithRestorationIdentifierPath:coder:, init 那里的视图控制器,并使用您需要从编码器中获取的任何内容来进行 pre-viewDidLoad 初始化。

+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder
{
    if ([[identifierComponents lastObject] isEqualToString:kViewControllerRestorationIdentifier]) {
        if ([coder containsValueForKey:kIDToRestore]) {
            // Can only restore if we have an ID, otherwise return nil.
            int savedId = [coder decodeIntegerForKey:kIDToRestore];
            ViewController *vc = [[ViewController alloc] init];
            [vc setThingId:savedId];
            return vc;
        }
    }

    return nil;
}

我发现尝试实现状态恢复在我的代码中显示了不好的编程实践,比如在viewDidLoad. 因此,虽然这可行(如果您不使用情节提要),另一个选择是重构您设置视图控制器的方式。不要使用标志,而是将代码段移动到它们自己的方法中并从两个地方调用这些方法。

于 2013-08-31T13:32:30.443 回答
5
@property (nonatomic) BOOL firstLoad;

- (void)viewDidLoad {
    [super viewDidLoad];
    self.firstLoad = YES;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if (self.firstLoad) {
        [self setupUI];
        self.firstLoad = NO;
    }
}

感谢@calvinBhai 的建议。

于 2013-08-12T09:47:14.193 回答
5

有趣的是,解码序列甚至完全不同:

 +viewControllerWithRestorationIdentifierPath:coder:
 awakeFromNib
 viewDidLoad
 decodeRestorableStateWithCoder:
 viewWillAppear
 viewDidAppear

像这样完全有道理。

于 2014-04-30T08:49:21.330 回答
2

摘自《iOS 9 编程:深入了解视图、视图控制器和框架》一书,第 386-387 页

状态恢复期间的已知事件顺序如下:

  1. application:shouldRestoreApplicationState:
  2. application:viewControllerWithRestorationIdentifierPath:coder:
  3. viewControllerWithRestorationIdentifierPath:coder:, 顺着链条
  4. viewDidLoad, 依次向下链;可能与上述内容交错
  5. decodeRestorableStateWithCoder:, 顺着链条
  6. application:didDecodeRestorableStateWithCoder:
  7. applicationFinishedRestoringState, 顺着链条

你仍然不知道什么时候到达viewWillAppear:,什么时候viewDidAppear:到达,或者是否viewDidAppear:会到达。但是applicationFinishedRestoringState你可以可靠地完成配置你的视图控制器和你的界面。

于 2017-05-26T20:19:47.873 回答
0

-decodeRestorableStateWithCoder:是的,如果之前被调用确实会更好-viewDidLoad。叹。

我将视图设置代码(取决于可恢复状态)移至-viewWillAppear:并使用dispatch_once(),而不是布尔变量:

private var setupOnce: dispatch_once_t = 0

override func viewWillAppear(animated: Bool) {
    dispatch_once(&setupOnce) {
        // UI setup code moved to here
    }

    :
}

该文档指出“视图不再在低内存条件下被清除”,因此dispatch_once在视图控制器的生命周期内应该是正确的。

于 2015-10-04T05:22:06.600 回答
0

添加到berbie的答案,

实际流程是:

 initWithCoder
 +viewControllerWithRestorationIdentifierPath:coder:
 awakeFromNib
 viewDidLoad
 decodeRestorableStateWithCoder:
 viewWillAppear
 viewDidAppear

请注意,在内部initWithCoder,您需要设置self.restorationClass = [self class];This 将强制viewControllerWithRestorationIdentifierPath:coder:被调用。

于 2016-12-12T04:16:49.390 回答
0

我注意到设置splitViewController.delegateinwillFinishLaunchingWithOptions会导致viewDidLoad更早地被调用。因此,如果将其移至两者,didFinishLaunchingWithOptions则可以- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder在调用 viewDidLoad 之前成功地在内部配置视图控制器。无论如何,在那里执行它可能对您很有用,因为您可以访问AppDelegate对象,persistentContainer.viewContext而不是需要使用恢复注册该对象,因此可以通过 ViewController 中的引用访问它- (void)decodeRestorableStateWithCoder:(NSCoder *)coder

于 2019-02-10T23:47:50.083 回答
-1

对 MixedCase 流程的一次更正(非常有帮助,谢谢),实际的呼叫流程有点不同:

这是方法调用的正常顺序:

从笔尖醒来

viewDidLoad

视图将出现

viewDidAppear

使用状态恢复时,这称为:

viewControllerWithRestorationIdentifierPath(解码常规启动所需的任何数据)

从笔尖醒来

viewDidLoad

视图将出现

viewDidAppear

decodeRestorableStateWithCoder(解码可恢复状态数据,并设置您的控制器 UI)

于 2014-04-29T17:11:48.930 回答