Почему высота UIPresentationController изменяется при наличии другого контроллера? - PullRequest
12 голосов
/ 13 мая 2019

Я использую UIPresentationController, чтобы показать нижний наконечник. Иногда presentationController может представлять другой контроллер. И когда представленный контроллер отклоняется, высота 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
    }
}

Мои коды, как показано ниже: enter image description here

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

enter image description here

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

enter image description here

1 Ответ

6 голосов
/ 15 мая 2019

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

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

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

Проблема, по-видимому (я использовал точки останова в методах 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 устанавливает кадр представления VC-представления.

* 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 перехода containerView:

class PresentBottom: UIPresentationController {

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

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

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

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

...