首页 文章

UIViewController生命周期调用与状态恢复相结合

提问于
浏览
9

我正在尝试在使用iOS 6和故事板的应用程序中实现状态恢复,但我在找到一种方法来防止对重方法的重复调用时遇到问题 .

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

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

这在正常的非状态恢复世界中工作正常 . 现在我添加了状态恢复,在恢复了一些属性后,我需要用这些属性更新UI:

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

那么现在发生的是,首先从 viewDidLoad 调用 setupUI 方法,然后再从 decodeRestorableStateWithCoder: 调用 . 我总是叫't see a method that I can override that' .

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

  • awakeFromNib

  • viewDidLoad

  • viewWillAppear

  • viewDidAppear

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

  • awakeFromNib

  • viewDidLoad

  • decodeRestorableStateWithCoder

  • viewWillAppear

  • viewDidAppear

我无法在 viewWillAppear 中调用 setupUI ,因为每当你回到视图时它也会被执行 .

如果 decodeRestorableStateWithCoder 被称为BEFORE viewDidLoad 会更方便,因为那时你可以使用恢复的属性 . 可悲的是,不是这样的,所以......当我知道我需要在 decodeRestorableStateWithCoder 之后重新做一遍时,我怎么能阻止在 viewDidLoad 中完成工作呢?

7 回答

  • -1

    如果您以编程方式进行状态恢复(即不使用故事板),则可以使用 + viewControllerWithRestorationIdentifierPath:coder: ,在那里初始化视图控制器并使用编码器所需的任何内容来执行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;
    }
    

    我发现尝试实现状态恢复已经在我的代码中显示出错误的编程实践,比如在_1460656中包含太多内容 . 所以虽然这有效(如果你重新设置你的视图控制器 . 而不是使用标志,将代码片段移动到他们自己的方法并从两个地方调用这些方法 .

  • 3

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

    +viewControllerWithRestorationIdentifierPath:coder:
     awakeFromNib
     viewDidLoad
     decodeRestorableStateWithCoder:
     viewWillAppear
     viewDidAppear
    

    它完全有道理 .

  • 2
    @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的建议 .

  • 0

    从“编程iOS 9:潜入深入视图,视图控制器和框架”一书386-387页

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

    • application:shouldRestoreApplicationState:

    • application:viewControllerWithRestorationIdentifierPath:coder:

    • viewControllerWithRestorationIdentifierPath:coder: ,顺序下行

    • viewDidLoad ,顺序下行;可能与上述内容交错

    • decodeRestorableStateWithCoder: ,顺序下行

    • application:didDecodeRestorableStateWithCoder:

    • applicationFinishedRestoringState ,顺序下行

    您仍然不知道 viewWillAppear:viewDidAppear: 何时到达,或者 viewDidAppear: 是否会到达 . 但是在 applicationFinishedRestoringState 中,您可以可靠地完成视图控制器和界面的配置 .

  • 5

    是的,如果在 -viewDidLoad 之前调用 -decodeRestorableStateWithCoder: ,那确实会更好 . 叹 .

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

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

    文档指出"views are no longer purged under low-memory conditions" so dispatch_once 应该在视图控制器的生命周期内是正确的 .

  • 0

    加入berbie的回答,

    实际流程是:

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

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

  • 6

    对MixedCase流程的一次修正(非常有帮助,谢谢),实际的调用流程有点不同:

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

    awakeFromNib

    viewDidLoad中

    viewWillAppear中

    viewDidAppear

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

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

    awakeFromNib

    viewDidLoad中

    viewWillAppear中

    viewDidAppear

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

相关问题