Pull to refresh

Comments 18

Вызвать функцию нужно в двух местах — в методе viewDidLoad и в viewWillTransition

Во viewDidLoad неправильно. Размеры не выставлены еще.
Вроде оно может зависеть от iOS и симулятора.


Сам использую viewWillLayoutSubviews для данной задачи.

Этот метод будет лейаутить без анимации. Правильнее через координатор.


Во viewDidLoad вполне можно, попробуй. SafeArea не выставлены, но размеры view контроллера уже корректные.

Корректные это какие? Такие же, как UIScreen.bounds? Но это лишь частный полноэкранный случай. Который также нарушается при реализации loadView() типа self.view = UIView(frame: .zero)

В этом случае сработает метод viewWillTransition. Не понимаю причин спора. Вы можете придумать случай когда рут-контроллер не на весь экран?

в чем профит использовать frame а не autolayout для таблицы?

Конкретно для одной таблицы — смысла нет, даже плохо (об этом есть и в видео, и в тексте).


Показал в ознакомительных целях

Предлагаю попробовать такой код:


let vc = ViewController()
vc.view.backroundColor = .red
let navigationController = UINavigationController.init(rootViewController: vc)
self.window = UIWindow.init(frame: UIScreen.main.bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()

и пересмотреть своё отношение к viewDidLoad и настройке navigationBar из чилда navigation контроллера

Если не сложно, поясните.


Настройка бара мне кажется вполне логичной. Особенно учитывая что бар подразумевает разные состояния и заголовки в зависимости от этого чилда.

Плохо, когда дочерний элемент управляет парентом, "хвост виляет собакой". Чилдов у UINavigationController может быть много, и если они наперебой будут что-то трогать в своём паренте, его состояние быстро станет неконсистентным. В данном случае стиль заголовков мог бы быть задан или в appDelegate после создания navigationController, или в каком-нибудь viewDidLoad наследника UINavigationController.


Хорошим примером обратной связи с navigationController является navigationItem. Заголовок экрана, заголовок backButton и всё остальное наполнение navigationBar прочитывается парентом-навконтроллером, и он же централизованно занимается лейаутом и наполнением своего navigationBar. А ведь всегда есть такой соблазн брать и вставлять разные данные в навбар где-то в viewWillAppear чилда, что является большой ошибкой.

Я все равно из ответа не понял как правильно установить разные стили для навигейшн бара православно)


Давайте возьмём ситуацию. У меня один навигейшн контроллер, и через него я показываю… пускай 15 контроллеров. У 7-ми из них большой и модный новый заголовок, у 8-ми — классический маленький.


Как стоит правильно сделать это?

Например сделать это дополнительным свойством у navigationItem, и обрабатывать его или в UINavigationDelegate, или в сабклассе UINavigationController.

Попробую привести аргументы.

  • Любая настройка стиля, вынесенная за пределы контролера (будь то большой / маленький навигейшн, заголовок и т.д.) обязательно должна вылиться в Switch-Сase или тому подобное. Не важно где, но Вы предлагаете проверять какой класс сейчас покажется и в зависимости от этого менять прааметры. Я вижу это неудобным — эти параметры связаны с самим контроллером.
  • Само наличие проперти навигейшн бара. К слову, опциональное. Нет никакой проблемы обратиться к опциональному проперти.
  • В примерах от Apple найдете работу именно через чилда.
  • Ваша метаформа с хвостом собаки некорректная, и сбивает. Во первых, она не учитывает опциональность. Второе и важное — чилдов много. У собаки много хвостов не бывает)


Как я понял, единственной мотивацией вынести найстройку навигейшн бара для конкретного контроллера из этого контроллера — Ваша метафора. Подумайте о следующих моментах:

  • У вас появляется отдельное место для настройки бара. Зачем? Макконел не одобрит.
  • Вы напишите больше кода
  • Работать будет не лучше. Читать код будет сложнее в силу его разрозненности


И это все ради метафоры, которая вообще не подходит к этому случаю.
Смысл собаки и хвоста в том, что navigationBar на самом деле никакого отношения к ViewController не имеет. Не может child управлять отображением парента, потому что:

* само наличие этого парента не гарантировано в любой момент времени. В данном примере есть код, который меняет стиль заголовков navigationBar во viewDidLoad чилда, хотя я привёл пример, когда viewDidLoad может вызваться и до того, как ViewController встроится в иерархию UINavigationController. Т.е. код в примере «надеется» на то, что viewDidLoad вызовется в ожидаемый момент времени, что часто нарушается на практике.

* ожидается, что `navigationController` – это первый и непосредственный парент ViewController. Т.е. есть уверенность в том, что этот NavigationController будет использовать navigationItem этого контроллера для настройки своего navigationBar. Этот факт тоже может сломаться, если, например, встроить похожий ViewController2 в ViewController. Если ViewController2 так же как и ViewController будет пытаться трогать navigationBar – они начнут друг с другом конкурировать за установку свойств. Потому что и у ViewController, и у ViewController2 будет одна и та же ссылка на navigationController. Получится так, что navigationController собирается отображать своим контентом ViewController, прочитает из его navigationItem нужные данные, настроит на их основе navigationBar и всё остальное, но какой-нибудь цвет фона navigationBar-у задаст child второго уровня (ViewController2).

Надеюсь, уже в этом случае нет сомнений в том, что ViewController2 не должен иметь совершенно никакого отношения к navigationBar.

* наличие проперти navigationController в UIViewController – это не аргумент. Ну вот так вот сделали, ещё во времена, когда не было контроллеров-контейнеров. Я склоняюсь к тому, что оно так сделано только лишь для простой возможности сделать «push», иначе разрабатывать стало бы на порядок сложнее (хоть и «правильнее»). Делая push из своего ViewController, приходится делать предположение, что он лежит в UINavigationController, что в любой момент времени может перестать быть правдой. У Apple была попытка добавить UIViewController.show(), но он тоже не очень-то помог.

* Макконел как раз-таки одобрит. Мой поинт в том, что нехорошо пытаться трогать чужую view, да ещё и родительскую. Допустим, в приложении нужно уметь задавать разные цвета фонов для navigationBar при переходах между экранами. Казалось бы, очевидное решение – делать это в каждом viewController где-нибудь в viewWillAppear.

Логика железная: зачем я буду эти цвета куда-то выносить, если они относятся только к этому контроллеру? Но нет, это не логика каждого чилда – это логика NavigationController! Это он должен в одной точке принимать решение какой цвет откуда взять и как выставить на _свой_ navigationBar, так как именно NavigationController владеет этой вьюхой.

Более того, в сетапе с viewWillAppear мы будем обязаны гарантировать, что обязательно каждый контроллер будет выставлять свой цвет фона бара, что невозможно сделать. Представим, у нас есть два пользовательских контроллера BlueBarViewController, GreenTitleViewController и один системный, например QLPreviewController.

* BlueBar – задаёт синий barTintColor у navigationBar
* GreenTitle – задаёт зелёный tintColor у navigationBar
* QLPreviewController – ничего никому не задаёт, живёт обычной жизнью, встраивается куда встраивают и просто отображает свой контент. И мы, слава богу, никак на это не можем повлиять.

Первым в иерархии появляется BlueBarController, меняет цвет фона на синий, пока всё хорошо.
Пушим GreenTitle, получаем синий цвет фона и зелёные тексты, уже наполовину хорошо.
Пушим QLPreviewController, и он тоже получает все эти спецэффекты.
Делаем pop на BlueBar – он из изначально «хорошего» своего состояния тоже оказывается «сломанным».

Всё потому, что точка управления navigationBar размазалась по всему приложению.

* и, наконец, переходим к «любая настройка стиля приводит к switch-case, что неудобно». На мой взгляд, это чуть ли не единственно верный путь. Во-первых, появляется намерение хотя бы явно сформулировать эти стили. Во-вторых, в итоге управление ими сконцентрируется в одном месте, и на более логичном уровне – уровне UINavigationController (т.к. именно он владеет navigationBar-ом, значит и управлять им должен тоже он). На мой взгляд, нужные настройки стилизации неплохо бы добавить в UINavigationItem (но это сложновато хакнуть), или же каждый ViewController может являться таким dataSource для NavigationController.

* финальная мысль: UIViewController внутри UINavigationController – это всего лишь контент. Его не должно заботить ни то, в каком он находится контейнере, ни какие у него размеры, ни как выглядят соседние с ним вьюхи. Общая стилизация (например цвет в navigationBar в зависимости от текущего viewController) может быть выполнена или на уровне UINavigationController (потому что он одновременно владеет своим баром, и знает про текущих чилдов), или же вообще где-то сбоку, на ещё более высоком уровне – например в UINavigationControllerDelegate.
Приветствую. А где можно посмотреть пример подобной реализации?
Согласен с комментаторами выше, по поводу написания этого всего в констрэйнтах. НО если хочется в фреймах, то зачем писать такой ужас «private func updateLayout(with size: CGSize) {
self.tableView.frame = CGRect.init(origin: .zero, size: size)
}» и вызывать его в 10 местах, если можно переопределить проперти intrinsicContentSize и указать размеры твоей таблицы
Я устал повторять — конкретно этот пример, конечно, лучше было сделать в констрейнтах. Я показал альтернативнвый способ. Мне часто приходится делать сложные лейауты, и поддержку два ориентаций. И не просто растягивать интерфейс, а именно менять расположение элементов. И, очевидно, сложные лейауты быстрее делаются при помощи фреймов.

Эту систему я показал в образовательных целях, конечно для примера она излишняя (очевидно что изляшняя).

Насчёт вызывать в разных местах — да, не вижу проблемы. Если вспомнить что ‘SadeAreaDidChange’ тоже провоцирует изменение лейаута, то нужно и там. Всего 3 места для вызова функции, но в итоге плавный лейаут при Семёне ориентации и больше возможностей кастомизации.

Обсуждение на самом деле ни о чем. Я согласен что констрейнты здесь уместны, и несколько раз сказал что альтернативный метод до ознакомительных целей. У какого опытного разработчика могла появится мысль что в этот примере такой способ уместен, не понимаю.
Это ужасный tutorial.

  1. Ок, вы пишите его для совсем начинающих, тогда непонятно, к чему пляска с плохим кодом в appDelegate и ручным добавлением таблицы в контролер: это отвлекает от непосредственно работы с таблицей и учит плохому.
  2. Код в numberOfRows — тоже страх и ужас, конечно. Зачем-то хотите проверить таблицу? Напишите guard. И тут, опять же, так как статья для новичков, то они же просто скопируют этот код к себе. А смысла в нем никакого, потому что, вот если серьезно, как часто у вас контролер является dataSource для нескольких таблиц? Если хотя бы раз, то вам стоит пересмотреть свое отношение к архитектуре. А если ни разу, то к чему проверка?
  3. У вас очень странная работа с фреймами, о чем вам уже написали выше. Но, помимо этого, в своем видео вы говорите, что фреймами удобнее, когда много вьюшек. Ха. Ха. Серьезно? Вы давно верстали фреймами сложные лейауты, особенно, если с поддержкой iPad, хотя сейчас и телефонов разнообразие достаточное для того, чтобы это превращалось в кошмар.
  4. Про стиль grouped. Если вы его попробуете использовать с несколькими секциями на длинной таблице, то увидите, чем это отличается от plain и почему же там ячейки сгруппированы.
  5. В случае с вашим примером использование UITableViewController никак бы не повредило пониманию, потому что методы вы бы реализовывали те же самые, а вот плясок вокруг ручного добавления таблицы не было бы.
  6. А какой смысл в force cast к TableViewCell, если конкретно из нее вы ничего все равно не ипользуете?
  7. Ну и минорное, но идентификаторы ячеек лучше выносить в константы, потому что они в коде повторяются, минимум, дважды. А так вероятность опечаток снижается, ну и понятнее, о чем речь.

В общем, я за то, чтобы в статьях для новичков писать сразу хороший код и приучать их к хорошему, чтобы потом не приходилось переучиваться.
Можно пример подобной реализации или ссылку на материал/видео где можно посмотреть?
Sign up to leave a comment.

Articles