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

Kev*_*ers 10 state uiviewcontroller ios

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

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

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
}
Run Code Online (Sandbox Code Playgroud)

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

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

那么现在发生的事情是首先setupUI调用方法viewDidLoad,然后再调用方法decodeRestorableStateWithCoder:.我没有看到一个我可以覆盖的方法,它始终被称为last.

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

  • awakeFromNib
  • viewDidLoad中
  • viewWillAppear中
  • viewDidAppear

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

  • awakeFromNib
  • viewDidLoad中
  • decodeRestorableStateWithCoder
  • viewWillAppear中
  • viewDidAppear

我无法将调用setupUI放入,viewWillAppear因为每次本机返回视图时它也会被执行.

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

nev*_*ing 6

如果您以编程方式进行状态恢复(即不使用故事板),则可以使用+ 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;
}
Run Code Online (Sandbox Code Playgroud)

我发现尝试实现状态恢复已经在我的代码中显示出错误的编程实践,比如包装过多viewDidLoad.因此,虽然这有效(如果您不使用故事板),另一个选择是重构您如何设置视图控制器.而不是使用标志,将代码片段移动到他们自己的方法,并从两个地方调用这些方法.


Kev*_*ers 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢@calvinBhai 的建议。

  • 最好使用反值,比如`self.firstLoaded`,它的默认值是`NO`,你没有理由在`viewDidLoad`中设置它。 (3认同)

ber*_*bie 5

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

 +viewControllerWithRestorationIdentifierPath:coder:
 awakeFromNib
 viewDidLoad
 decodeRestorableStateWithCoder:
 viewWillAppear
 viewDidAppear
Run Code Online (Sandbox Code Playgroud)

它完全有道理.