UIPageViewController的setViewController在childViewController中调用意外的视图生命周期

时间:2018-02-06 23:45:22

标签: ios swift uipageviewcontroller

在展示ViewController时,我希望视图生命周期符合以下顺序:

  1. viewDidLoad中()
  2. viewWillAppear中
  3. viewWillLayoutSubviews()
  4. viewDidLayoutSubviews()
  5. viewDidAppear()
  6. 注意:4到5之间可以是viewWill / viewDidLayoutSubviews()的更多迭代。换句话说,我希望viewDidLayoutSubview在第一个viewDidAppear之前触发。

    为了测试这个,我将一个childViewController添加到一个UIViewController子类,并调用addSubview来添加childViewController的视图。

    override func viewDidLoad() {
        super.viewDidLoad()
    
        let childVC = ChildViewController()
        self.addChildViewController(childVC)
        self.view.addSubview(childVC.view)
    
    }
    

    当我打印出视图方法时,这会产生预期的结果:

    1. viewDidLoad中()
    2. viewWillAppear中
    3. viewWillLayoutSubviews()
    4. viewDidLayoutSubviews()
    5. viewWillLayoutSubviews()
    6. viewDidLayoutSubviews()
    7. viewDidAppear
    8. 当我在UIPageViewController子类上使用setViewController时:

      override func viewDidLoad() {
          super.viewDidLoad()
      
          let childVC = ChildViewController()
          self.setViewControllers([childVC], direction: .forward, animated: true, completion: nil)
      
      }
      

      我总是在第一个LayoutSubviews方法之前执行viewDidAppear:

      1. viewDidLoad中()
      2. viewWillAppear中
      3. viewDidAppear
      4. viewWillLayoutSubviews()
      5. viewDidLayoutSubviews()
      6. viewWillLayoutSubviews()
      7. viewDidLayoutSubviews()
      8. 有人可以解释为什么会出现这种情况吗?

        ----进一步观察---

        我还在childViewController的日志中添加了willMove和DidMove方法。初步测试的结果:

        willMove(toParentViewController:)

        1. viewDidLoad中()
        2. viewWillAppear中
        3. viewWillLayoutSubviews()
        4. viewDidLayoutSubviews()
        5. viewWillLayoutSubviews()
        6. viewDidLayoutSubviews()
        7. viewDidAppear
        8. UIPageViewController的结果:

          1. viewDidLoad中()
          2. viewWillAppear中
          3. viewDidAppear didMove(toParentViewController:)
          4. viewWillLayoutSubviews()
          5. viewDidLayoutSubviews()
          6. viewWillLayoutSubviews()
          7. viewDidLayoutSubviews()
          8. 因此看起来好像setViewController调用了moveToParentViewController,这可能是它之前调用viewDidAppear的原因。

1 个答案:

答案 0 :(得分:0)

setViewControllers调用此方法:

/* @class UIPageViewController */ -(void)_setViewControllers:(void *)arg2 withCurlOfType:(long long)arg3 fromLocation:(struct CGPoint)arg4 direction:(long long)arg5 animated:(bool)arg6 notifyDelegate:(bool)arg7 completion:(void *)arg8 {

[r13 _child:r14 beginAppearanceTransition:rcx animated:r8];
r14 = [_objc_msgSend_1426728(r13, @selector(view)) retain];
rax = _objc_msgSend_1426728(var_D0, @selector(view));
rax = [rax retain];
[r14 addSubview:rax];
r13 = var_B8;
[rax release];
rdi = r14;
r14 = var_D0;
[rdi release];
[r13 _childEndAppearanceTransition:r14];

开始和结束看起来像这样:

/* @class UIPageViewController */
-(void)_child:(void *)arg2 beginAppearanceTransition:(bool)arg3 animated:(bool)arg4 {
    r14 = arg4;
    r15 = arg3;
    r12 = [arg2 retain];
    if ([self _forwardAppearanceMethods] == 0x0) {
            objc_unsafeClaimAutoreleasedReturnValue([r12 view]);
            [r12 beginAppearanceTransition:r15 & 0xff animated:r14 & 0xff];
    }
    [r12 release];
    return;
}

如上所述,对_forwardAppearanceMethods的调用必须返回0,这会引起问题,并查看其作用:

/* @class UIViewController */
-(bool)_forwardAppearanceMethods {
    r14 = self;
    rax = objc_opt_class(self, _cmd);
    rbx = @selector(shouldAutomaticallyForwardAppearanceMethods);
    rdx = rbx;
    if ([rax doesOverrideViewControllerMethod:rdx] == 0x0) {
            rbx = @selector(automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers);
    }
    rax = *_objc_msgSend;
    rax = (rax)(r14, rbx, rdx, *_objc_msgSend);
    return rax;
}

我们可以看到它的调用shouldAutomaticallyForwardAppearanceMethods已实现为低位UIPageViewController,如下所示:

/* @class UIPageViewController */
-(bool)shouldAutomaticallyForwardAppearanceMethods {
    return 0x0;
}

请注意,它还具有:

/* @class UIPageViewController */
-(bool)shouldAutomaticallyForwardRotationMethods {
    return 0x0;
}

使用Hopper

完成反编译