Почему высота UIPresentationController изменяется при наличии другого контроллера?

Я использую UIPresentationController, чтобы показать нижний наконечник. Иногда presentationController может представлять другой контроллер. И когда представленный контроллер уволен, высота презентационного контроллера была изменена. Итак, почему он изменился и как я могу решить эту проблему. Код нравится ниже:

class ViewController: UIViewController {

    let presentVC = UIViewController()
    override func viewDidLoad() {
        super.viewDidLoad()
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
            guard let `self` = self else { return }
            self.presentBottom(self.presentVC)
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
            guard let `self` = self else { return }
            let VC = UIViewController()
            VC.view.backgroundColor = .red
            self.presentVC.present(VC, animated: true, completion: {

                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    VC.dismiss(animated: true, completion: nil)
                }

            })
        }
    }

}
public class PresentBottom: UIPresentationController {

    public override var frameOfPresentedViewInContainerView: CGRect {
        let bottomViewHeight: CGFloat = 200.0
        let screenBounds              = UIScreen.main.bounds
        return CGRect(x: 0, y: screenBounds.height - bottomViewHeight, width: screenBounds.width, height: bottomViewHeight)
    }

}
extension UIViewController: UIViewControllerTransitioningDelegate {

    public func presentBottom(_ vc: UIViewController ) {
        vc.view.backgroundColor = .purple
        vc.modalPresentationStyle = .custom
        vc.transitioningDelegate = self
        self.present(vc, animated: true, completion: nil)
    }

    public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        let vc = PresentBottom(presentedViewController: presented, presenting: presenting)
        return vc
    }
}

Мои коды, как показано ниже: введите здесь описание изображения

Высота presentationController показана прямо на изображении ниже:

введите описание изображения здесь

Что меня смутило, так это то, что высота текущего контроллера представления изменена:

введите описание изображения здесь


person dowZhang    schedule 13.05.2019    source источник
comment
Похож на этот вопрос >UIAlertController перемещается в ошибочное положение вверху экрана при вызове presentViewController   -  person dowZhang    schedule 15.05.2019


Ответы (1)


По умолчанию, когда представлен контроллер представления, UIKit удаляет представляющие VC после завершения анимации. Представление представляющего VC только временно добавляется к представлению контейнера во время перехода.

Вы можете изменить это поведение с помощью UIModalPresentationStyle.overFullScreen или с помощью shouldRemovePresentersView в случае индивидуальная презентация. В этом случае только представленный вид перемещается во временный containerView, и вся анимация выполняется над представлением представляющего VC — ни один из нижних видов не обрабатывается.

Здесь вы выполняете переход fullScreen над VC, представленным с пользовательским переходом. UIKit сохраняет иерархию представлений нетронутой и только перемещает представление представляющего VC (фиолетовое) вместе с представлением представленного VC (красное) в представление контейнера во время переходов отклонения и презентации. Затем он удаляет фиолетовый, как только анимация завершена.

По-видимому, проблема (я использовал точки останова в методах didMoveToSuperview и setFrame), когда происходит переход отклонения, «встроенный» контроллер анимации по умолчанию не заботится о предыдущем кадре представления представления VC, когда он временно добавляет его в containerView. Он устанавливает его во фрейм containerView сразу после перехода в containerView.

Я не знаю, это ошибка или преднамеренный выбор.

Вот код, который я использовал:

class BottomVCView: UIView {
    override var frame: CGRect {
        set {
            super.frame = newValue // BREAKPOINT : newValue.height == 568.0
        }
        get {
            return super.frame
        }
    }
}

class BottomVC: UIViewController {

    lazy var myView = BottomVCView()

    override func loadView() {
        view = BottomVCView()
    }
}

А вот и стек, где UIViewControllerBuiltinTransitionViewAnimator задает фрейм представления представления ВК.

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000102ca0ce8 Project`BottomVCView.frame.setter(newValue=(origin = (x = 0, y = 0), size = (width = 320, height = 568)), self=0x00007ffde5e059f0) at ViewController.swift:35:13
    frame #1: 0x0000000102ca0c9f Project`@objc BottomVCView.frame.setter at <compiler-generated>:0
    frame #2: 0x0000000108e2004f UIKitCore`-[UIViewControllerBuiltinTransitionViewAnimator animateTransition:] + 1149
    frame #3: 0x0000000108d17985 UIKitCore`__56-[UIPresentationController runTransitionForCurrentState]_block_invoke + 3088
    frame #4: 0x0000000109404cc9 UIKitCore`_runAfterCACommitDeferredBlocks + 318
    frame #5: 0x00000001093f4199 UIKitCore`_cleanUpAfterCAFlushAndRunDeferredBlocks + 358
    frame #6: 0x000000010942132b UIKitCore`_afterCACommitHandler + 124
    frame #7: 0x00000001056fe0f7 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    frame #8: 0x00000001056f85be CoreFoundation`__CFRunLoopDoObservers + 430
    frame #9: 0x00000001056f8c31 CoreFoundation`__CFRunLoopRun + 1505
    frame #10: 0x00000001056f8302 CoreFoundation`CFRunLoopRunSpecific + 626
    frame #11: 0x000000010d0dd2fe GraphicsServices`GSEventRunModal + 65
    frame #12: 0x00000001093f9ba2 UIKitCore`UIApplicationMain + 140
    frame #13: 0x0000000102ca60fb NavBar`main at AppDelegate.swift:12:7
    frame #14: 0x0000000106b9f541 libdyld.dylib`start + 1
    frame #15: 0x0000000106b9f541 libdyld.dylib`start + 1

Если вы используете containerViewWillLayoutSubviews, вы можете вернуть пурпурному фрейму представления его правильное значение, но это произойдет только после того, как будет выполнен переход отклонения и представление будет удалено из контейнера перехода fullScreen:

class PresentBottom: UIPresentationController {

    override func containerViewWillLayoutSubviews() {
        super.containerViewWillLayoutSubviews()
        presentedView?.frame = frameOfPresentedViewInContainerView
    }
}

Я думаю, вы можете просто использовать:

let VC = UIViewController()
VC.view.backgroundColor = .red
VC.modalPresentationStyle = .overFullScreen

Чтобы сохранить всю иерархию представлений на месте. Аниматор не будет касаться представления представляющего VC - viewController(for: .from) возвращает nil, так как его представление не перемещается в представление контейнера.

person GaétanZ    schedule 15.05.2019