Сохраняйте вид при переходе pu sh между контроллерами вида - PullRequest
2 голосов
/ 07 января 2020

У меня есть приложение с панелью вкладок iOS. Одной из вкладок является карта (MyMapViewController). В верхней части MyMapViewController:

enter image description here

имеется пользовательская панель «Поиск». Как только пользователь нажимает на панель «Поиск», он получает на экране поиска:

enter image description here

Теперь пользователь может вводить некоторые имена, и список объектов фильтруется и позволяет пользователю найти нужный объект. Все работает просто отлично.

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

enter image description here

В настоящее время экран поиска является контроллером дочернего представления MyMapViewController. Это называется MySearchViewController. Анимированный переход между режимом «карта» и режимом «поиск» выполняется с помощью Core Animation. На контроллерах представления нет никаких операций «pu sh» / «pop» или «present» / «dismiss».

Я не могу скрыть панель вкладок (UITabBar), сделав ее isHidden = true или смещая его рамку, потому что он оставляет пустой прямоугольник. Как я знаю, есть только два способа скрыть панель вкладок:

  1. pu sh новый контроллер (с hidesBottomBarWhenPushed = true) в стек навигации
  2. представляет модал контроллер

Так что, кажется, мне нужно переделать из

(родительский контроллер представления) MyMapViewController, (дочерний контроллер представления) MySearchViewController

в

UINavigationStack: MyMapViewController - (pu sh) -> MySearchViewController

Но. В таком случае как мне поступить с панелью поиска? Это часть MyMapViewController, а также часть MySearchViewController. Возможно ли для представления быть частью двух UIViewControllers? Кроме того, он мне нужен, чтобы немного оживить во время нажатия перехода от MyMapViewController до MySearchViewController (как вы видите, увеличенное стекло должно преобразоваться в стрелку назад).

Ответы [ 2 ]

2 голосов
/ 15 января 2020

Проблема с UITabBarController заключается в том, что его TabBar добавляется без использования NSLayoutConstraints (или, точнее, он переводит маску автоматического изменения размера в ограничения). По этой причине вы можете использовать два вида подхода:

1) Используйте UITabBarController так, как вы это делаете сейчас, но для его скрытия нужны некоторые хаки - в основном используйте UITabBarController внутри UINavigationController, чтобы получить sh представление поверх него (но переход будет виден, даже если вы сделаете sh без анимации (клавиатура начнет скрываться), или вы можете скрыть TabBar и изменить размер фрейма представления содержимого TabBar вручную, как показано на рисунке { ссылка }).

В этом последнем случае вы также должны запомнить кадр представления содержимого перед его изменением (или рассчитать его перед повторным отображением TabBar). Кроме того, поскольку это не является официальным API, вы должны принять во внимание, что порядок подпредставлений внутри UITabBarController может измениться, а эффекты могут выглядеть очень странно (или просто создать sh приложение)

2) use " обычный "UIViewController с UITabBar и его элементы, добавленные вручную с ограничениями. Это может быть также пользовательский подкласс UIView и несколько кнопок, созданных из XIB. Здесь вы создаете ограничения напрямую, поэтому у вас есть лучший контроль. Но этот также не будет go без некоторых хаков, потому что UITabBar, добавленный к одному UIViewController, идет вместе с этим UIViewController с каждым переходом (учитывая, что у вас есть UINavigationController в каждом UIViewController, это будет очень часто).

Так в этом случае основной проблемой является создание единой нижней панели и ее передача в UIWindow в viewDidAppear вида, где создается ваша единственная нижняя панель - рекомендуется из раскадровки или из файла xib. Для следующего просмотра вы только передадите ссылку на него или сохраните этот указатель в одном классе для этого. Не забудьте также создать представление, охватывающее безопасную область под панелью вкладок.

Это будет выглядеть так:

    private var firstRun = false

    override func viewDidLoad() {
        super.viewDidLoad()
        firstRun = true
}

    override func viewDidAppear(_ animated: Bool) {
                super.viewDidAppear(animated)
                guard firstRun else {
                    bottomBar.superview?.bringSubviewToFront(bottomBar)
                    bottomSafeAreaView.superview?.bringSubviewToFront(bottomSafeAreaView)
                    return
                }

                guard let window = UIApplication.shared.windows.first, let bottomB = bottomBar, let bottomSafeArea = bottomSafeAreaView else { return }

                if bottomB.superview != window {
                    bottomB.deactivateConstrainsToSuperview()
                    bottomSafeArea.deactivateConstrainsToSuperview()

                    window.addSubview(bottomSafeArea)
                    window.addSubview(bottomB)
                    let bottomLeft = NSLayoutConstraint(item: bottomSafeArea, attribute: .leading, relatedBy: .equal, toItem: window, attribute: .leading, multiplier: 1, constant: 0)
                    let bottomRight = NSLayoutConstraint(item: bottomSafeArea, attribute: .trailing, relatedBy: .equal, toItem: window, attribute: .trailing, multiplier: 1, constant: 0)
                    let bottomBottom = NSLayoutConstraint(item: bottomSafeArea, attribute: .bottom, relatedBy: .equal, toItem: window, attribute: .bottom, multiplier: 1, constant: 0)
                    let leftConstraint = NSLayoutConstraint(item: bottomB, attribute: .leading, relatedBy: .equal, toItem: window, attribute: .leading, multiplier: 1, constant: 0)
                    let rightConstraint = NSLayoutConstraint(item: bottomB, attribute: .trailing, relatedBy: .equal, toItem: window, attribute: .trailing, multiplier: 1, constant: 0)
                    let bottomConstraint = NSLayoutConstraint(item: bottomB, attribute: .bottom, relatedBy: .equal, toItem: bottomSafeArea, attribute: .top, multiplier: 1, constant: 0)
                    NSLayoutConstraint.activate([bottomLeft, bottomRight, bottomBottom, leftConstraint, rightConstraint, bottomConstraint])
                }

                window.layoutIfNeeded()

                DispatchQueue.main.async(execute: {
                    bottomB.superview?.bringSubviewToFront(bottomB)
                    bottomSafeArea.superview?.bringSubviewToFront(bottomSafeArea)
                })

                firstRun = false
            }

Плюс один вспомогательный метод, созданный в расширении:

extension UIView {

    func deactivateConstrainsToSuperview() {
        guard let superview = self.superview else {return}
        NSLayoutConstraint.deactivate(self.constraints.filter({
            return ($0.firstItem === superview || $0.secondItem === superview)
        }))
    }
}

Итак, немного кода для написания, но только один раз. После этого у вас будет TabBar, который легко отобразить или скрыть при необходимости, используя ограничение между «представлением содержимого» и безопасной областью таким образом

private func hideBottomBar() {
        UIView.animate(withDuration: Constants.appAnimation.duration, animations: { [weak self] in
            guard let self = self else { return }
            self.bottomBar.isHidden = true
            self.bottomBarHeightConstraint.constant = 0
            self.bottomBar.superview?.layoutIfNeeded()
        })
    }

и

private func showBottomBar() {
        UIView.animate(withDuration: Constants.appAnimation.duration, animations: { [weak self] in
            guard let self = self else { return }
            self.bottomBar.isHidden = false
            self.bottomBarHeightConstraint.constant = Constants.appConstraintsConstants.bottomBarHeight
            self.bottomBar.superview?.layoutIfNeeded()
        })
    }

, как для высота обзора, охватывающего безопасную область (между нижней частью tabBar и верхней частью BottomLayoutGuide)

if #available(iOS 11.0, *) {
                self.bottomSafeAreaViewHeightConstraint.constant = self.view.safeAreaInsets.bottom
            } else {
                self.bottomSafeAreaViewHeightConstraint.constant = 0
            }

Надеюсь, это будет полезно, удачи!

0 голосов
/ 11 января 2020

Если вам нужно скрыть панель тапов, вы можете сделать это через self.tabBarController?.tabBar.isHidden = true.

Если вы хотите использовать его из SearchViewController (который не представлен или pu sh), вам нужно добавить SearchViewController в иерархию просмотра контроллеров через self.addChild(searchVC) и добавьте self.tabBarController?.tabBar.isHidden = true self.tabBarController?.tabBar.setNeedsLayout()

...