Я реализую свой собственный UIPopoverController
.
(Вот тестовый проект, если вы хотите увидеть ошибку: https://github.com/d3mueller/PopupController-Test)
Моя версия использует child viewController
, чтобы показать всплывающее окно / всплывающее окно вместо его представления.
Это работает, в основном. Тем не менее, есть одна странная ошибка, которая возникает, когда клавиатура отклоняется (когда она отображается, поповер перемещается вверх, чтобы компенсировать, а после этого он должен двигаться вниз):
Если viewController
(который я хочу показать как всплывающее окно) - это UINavigationController
, тогда UINavigationBar
странным образом опускается при отклонении клавиатуры:
(голубая полоса - navigation bar
)
Понятия не имею, что там происходит. Я не могу понять это. Почему он это делает?
Спасибо за любую помощь с этим:)
(Следующий код из класса Popup
в моем тестовом проекте, связанном выше:)
class Popup {
// MARK: - Properties
// ========== PROPERTIES ==========
private var isPresenting: Bool {
return presentingViewController != nil && presentedViewController != nil
}
private weak var presentingViewController: UIViewController? = nil
private weak var presentedViewController: UIViewController? = nil
public var padding: CGFloat = 10
public var size: CGSize = CGSize(width: 375, height: 1000)
private lazy var targetLocator: UIView = {
let targetLocator = UIView()
targetLocator.accessibilityIdentifier = "targetLocator"
targetLocator.isUserInteractionEnabled = false
targetLocator.alpha = 0.8
targetLocator.translatesAutoresizingMaskIntoConstraints = true
return targetLocator
}()
private lazy var holderView: UIView = {
let view = UIView()
view.accessibilityIdentifier = "HolderView"
view.backgroundColor = .gray
view.layer.masksToBounds = true
view.layer.cornerRadius = 10
view.layer.zPosition = 5
return view
}()
private lazy var tapRecognizerView: UIView = {
let view = UIView()
view.isUserInteractionEnabled = true
view.addGestureRecognizer(self.tapGestureRecognizer)
return view
}()
private var centerOffset: CGPoint = .zero
private lazy var tapGestureRecognizer: UITapGestureRecognizer = {
return UITapGestureRecognizer(target: self, action: #selector(handleTap(tap:)))
}()
private var bottomConstraint: Constraint? = nil
// ====================
// MARK: - Initializers
// ========== INITIALIZERS ==========
init() {
// Add Listener for keyboard
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(notification:)), name: UIWindow.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: UIWindow.keyboardWillHideNotification, object: nil)
}
deinit {
// Remove observers
NotificationCenter.default.removeObserver(self)
}
// ====================
// MARK: - Overrides
// ========== OVERRIDES ==========
// ====================
// - TODO: Arrow an der Seite, auf targetView zeigend
// - TODO: Comfort funktion für contentSize
// - TODO: Auf Tastatur-Erscheinen reagieren (buggy, navigation bar verschiebt sich irgendwie)
// - TODO: "Close" button in settings vc darf nicht dismissen!
// MARK: - Functions
// ========== FUNCTIONS ==========
public func present(_ presentedVC: UIViewController, on presenting: UIViewController, target targetView: UIView) {
guard let view = presenting.view else { return }
self.presentingViewController = presenting
self.presentedViewController = presentedVC
// Add cellLocator (target view might be a cell that loses its position at some point)
// This uses translatesAutoresizingMaskIntoConstraints to have proper constraints
view.addSubview(targetLocator)
targetLocator.frame = targetView.convert(targetView.bounds, to: view)
tapRecognizerView.frame = view.frame
tapGestureRecognizer.isEnabled = true
view.addSubview(tapRecognizerView)
tapRecognizerView.snp.remakeConstraints { (make) in
make.edges.equalToSuperview()
}
// Add the holder view
view.addSubview(holderView)
// Do not use targetView.center as that use from the superview's coordinate system
let targetViewCenter = targetView.convert(CGPoint(x: targetView.bounds.width / 2, y: targetView.bounds.height / 2), to: view)
centerOffset = CGPoint(x: targetViewCenter.x - view.center.x, y: targetViewCenter.y - view.center.y)
holderView.snp.remakeConstraints { (make) in
make.leading.greaterThanOrEqualToSuperview().offset(padding)
make.trailing.lessThanOrEqualToSuperview().offset(-padding)
make.top.greaterThanOrEqualTo(view.safeAreaLayoutGuide.snp.top).offset(padding)
self.bottomConstraint = make.bottom.lessThanOrEqualTo(view.safeAreaLayoutGuide.snp.bottom).offset(-padding).constraint
make.centerY.equalTo(targetLocator).priority(750)
make.width.equalTo(self.size.width)
make.height.equalTo(self.size.height).priority(750)
if centerOffset.x >= 0 {
// left
make.trailing.equalTo(targetLocator.snp.leading).offset(-10).priority(750)
} else {
// right
make.leading.equalTo(targetLocator.snp.trailing).offset(10).priority(750)
}
}
// Animate it in
holderView.alpha = 0.0
holderView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
UIViewPropertyAnimator(duration: 0.3, dampingRatio: 0.8) {
self.holderView.transform = .identity
self.holderView.alpha = 1.0
}.startAnimation()
// Add child VC
presenting.addChild(presentedVC)
holderView.addSubview(presentedVC.view)
presentedVC.didMove(toParent: presenting)
presentedVC.view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
public func dismiss() {
guard let presentedViewController = self.presentedViewController else { return }
// Animate out the holderView
let animator = UIViewPropertyAnimator(duration: 0.3, dampingRatio: 0.8) {
self.holderView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
self.holderView.alpha = 0.0
}
animator.addCompletion { (_) in
// Remove centerOffset
self.centerOffset = .zero
// Remove targetLocator
self.targetLocator.removeFromSuperview()
// Remove tapRecognizerView
self.tapRecognizerView.removeFromSuperview()
// Remove child vc (that's being presented)
presentedViewController.willMove(toParent: nil)
presentedViewController.view.removeFromSuperview()
presentedViewController.removeFromParent()
// Remove holderView
self.holderView.removeFromSuperview()
}
animator.startAnimation()
}
@objc private func handleTap(tap: UITapGestureRecognizer) {
tapGestureRecognizer.isEnabled = false
dismiss()
}
@objc private func keyboardWillShow(notification: Notification) {
guard isPresenting else { return }
let keyboardSize = (notification.userInfo? [UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let keyboardHeight: CGFloat = keyboardSize?.height ?? 0
// This simply updates the bottom constraint
self.bottomConstraint?.update(offset: -padding - keyboardHeight)
UIViewPropertyAnimator(duration: 2.4, dampingRatio: 0.8) {
self.presentingViewController?.view.layoutIfNeeded()
}.startAnimation()
}
@objc private func keyboardWillHide(notification: Notification) {
guard isPresenting else { return }
self.bottomConstraint?.update(offset: -padding)
UIViewPropertyAnimator(duration: 2.4, dampingRatio: 0.8) {
self.presentingViewController?.view.layoutIfNeeded()
}.startAnimation()
}
// ====================
}
EDIT:
Так что это не так, когда я изменяю код перехода вниз:
@objc private func keyboardWillHide(notification: Notification) {
guard isPresenting else { return }
self.bottomConstraint?.update(offset: -padding)
// New Line (This way it works but it obviously messes up the animation
self.presentingViewController?.view.layoutIfNeeded()
//
UIViewPropertyAnimator(duration: 2.4, dampingRatio: 0.8) {
self.presentingViewController?.view.layoutIfNeeded()
}.startAnimation()
}
Но это не очень хорошее решение