Как реализовать пользовательский NavigationController в Swift - PullRequest
0 голосов
/ 13 октября 2018

У меня есть приложение, которое использует NavigationController и TabBarController для удобной навигации по различным представлениям.Для некоторых видов у меня есть заголовок и несколько кнопок (иногда только справа, иногда справа и слева).Однако для некоторых просмотров, например, при просмотре чьего-либо профиля, я не хочу устанавливать заголовок.Я хочу показать изображение профиля и добавить имя пользователя под изображением.

Мой вопрос довольно прост: должен ли я использовать NavigationController для этого, так как я хочу, чтобы изображение профиля проходило «над»NavigationController, что делает его всего 16 пикселями из строки состояния, а НЕ из NavigationController.Было бы это возможно, при этом все еще имея левую и правую кнопки (например, для редактирования профиля, если он у вас есть, или для кнопки «дополнительные параметры»).

Я думал об использовании совершенно другого подходагде я не использую NavigationController, но вместо этого использую метку для отображения заголовка, если необходимо, и добавляю вид слева и справа, где я мог бы добавить кнопки при необходимости.

Так как это первыйРаз я делаю что-то вроде этого, мне бы очень хотелось услышать ваше мнение о том, как достичь этого результата и какова лучшая практика.Что-то подсказывает мне, что было бы лучше использовать NavigationController, так как это то, что он должен делать, но как я могу затем реализовать необязательный заголовок и просмотреть его при необходимости?Я уже знаю, как удалить нижнюю линию волос и как правильно установить цвет фона, чтобы это не было самой большой проблемой.Я просто обеспокоен тем, как показать изображение через NavigationController.

Спасибо, ребята!

1 Ответ

0 голосов
/ 14 октября 2018

Лучший способ создать собственную "titleLabel" - это создать подклассы UIView и UINavigationController.Демонстрация этого: здесь .

Основные части с контроллером навигации.По сути, вы хотите оставить свойство title пустым (пустая строка или просто не устанавливать его), и, поскольку UINavigationController является просто подклассом UIViewController, работающим как контейнерное представление с возможностями push / pop, работайтес вашим подклассом таким образом.В моей демонстрации мой подклассный класс контроллера навигации называется NavigationController.

На мой взгляд, главное преимущество, которое вы получаете от создания подкласса UINavigationController , - это , использующее возможности push / pop ..... в то время как вы можете использовать сегменты, они требуют раскадровки, и в то время как вы можете использовать анимацию UIView, она просто не кажется мне "гладкой".

(1) Если вы нене работаете со раскадровкой, не забудьте установить этот подкласс в качестве корневого контроллера представления в AppDelegate:

var navController: NavigationController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    self.window = UIWindow(frame: UIScreen.main.bounds)
    navController = NavigationController(nibName: nil, bundle: nil)
    self.window!.rootViewController = navController
    self.window?.makeKeyAndVisible()
    return true
}

(2) Хотя есть другие способы сделать это, мойпредпочтение отдается созданию экземпляров контроллеров представления в моем подклассе, предоставляя при необходимости методы push / pop:

class NavigationController:UINavigationController {

    let redVC = RedViewController()
    let greenVC = GreenViewController()
    let titleView = TitleView()
    var titleViewHeightConstraint:NSLayoutConstraint!

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        pushViewController(redVC, animated: false)  // display the first VC
    }
    func popGreenVC() {
        popViewController(animated: true)
    }
    func pushGreenVC() {
        pushViewController(greenVC, animated: true)
    }
}

(3) В других VC установите ваши кнопки влево / вправо какнужно и нажмите / поп по мере необходимости.Мне нравится пользовательская «стрелка» с использованием символов UTF-8:

class RedViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.red
        let showGreenVC = UIBarButtonItem(title: "Green \u{25B6}", style: .plain, target: self, action: #selector(pushGreenVC))
        self.navigationItem.rightBarButtonItem = showGreenVC
    }
    @objc func pushGreenVC() {
        let navController = self.navigationController as! NavigationController
        navController.pushGreenVC()
    }
}

class GreenViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.green
        let showRedVC = UIBarButtonItem(title: "\u{25C0} Red", style: .plain, target: self, action: #selector(popGreenVC))
        self.navigationItem.leftBarButtonItem = showRedVC
    }
    @objc func popGreenVC() {
        let navController = self.navigationController as! NavigationController
        navController.popGreenVC()
    }
}

Обратите внимание, что я принудительно приводю свойство представления naviagtionController? в качестве моего подкласса.Приведение необходимо, чтобы получить доступ к пользовательским методам в нем.

(4) Далее, включите ваш собственный "titleView".Для демонстрации я создал 50/50 желто-синий.Обратите внимание, что он использует якоря автоматического макета:

class TitleView: UIView {
    let yellowView = UIView()
    let blueView = UIView()
    convenience init() {
        self.init(frame: CGRect.zero)

        self.translatesAutoresizingMaskIntoConstraints = false
        yellowView.translatesAutoresizingMaskIntoConstraints = false
        blueView.translatesAutoresizingMaskIntoConstraints = false

        yellowView.backgroundColor = UIColor.yellow
        blueView.backgroundColor = UIColor.blue
        self.addSubview(yellowView)
        self.addSubview(blueView)

        yellowView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        yellowView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        yellowView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5).isActive = true
        yellowView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        blueView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        blueView.leadingAnchor.constraint(equalTo: yellowView.trailingAnchor).isActive = true
        blueView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5).isActive = true
        blueView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
    }
}

(5) В init контроллера Nav добавьте код, чтобы добавить вид заголовка.Обратите внимание, что я использую автоматическую разметку с двумя вещами.Во-первых, я устанавливаю ширину равной 1/3 ширины строки состояния.Вы можете экспериментировать - с изображением вам лучше установить постоянную ширину.Во-вторых, обратите внимание на использование «именованного» ограничения, называемого titleViewHeightConstraint.Я объясню это на следующем шаге.

titleViewHeightConstraint = titleView.heightAnchor.constraint(equalToConstant: 0)
titleViewHeightConstraint.isActive = true
titleView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
titleView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.3).isActive = true

(5) Сделайте высоту вашей метки заголовка динамической:

Помните, что UINavigationController - этона самом деле контроллер представления контейнера - это все аспекты.Это «корневое» представление в полноэкранном режиме, поэтому, если вы прикрепите метку заголовка к нижней части, вы получите метку вертикального размера сверху вниз.Хуже того, иногда есть строка состояния, особенно если она портретная.Но в других случаях (по умолчанию) это не так.По той причине, что я использую safeAreaLayoutGuide из и навигационный контроллер (который обнаруживает наличие строки состояния) и контроллеры вида внутри него (который определяет высотупанель навигации ПЛЮС строка состояния).

(5a) Добавьте этот метод к контроллеру навигации:

func changeTitleViewHeight(to:CGFloat) {
    titleViewHeightConstraint.constant = to - view.safeAreaInsets.top
}

(5b) и добавьте это переопределениедля всех контроллеров представления внутри субклассированного навигационного контроллера:

override func viewSafeAreaInsetsDidChange() {
    let navController = self.navigationController as! NavigationController
    navController.changeTitleViewHeight(to: view.safeAreaInsets.top)
}
...