- PVSM.RU - https://www.pvsm.ru -
Этот пост является вольным переводом статьи Xcode: A Better Way to Deal with Storyboards [1] by Stan Ostrovskiy [2]
Некоторые примеры кода в оригинальной статье устарели (ввиду выхода Swift 3) и в переводе были изменены.
Советы и рекомендации по работе с Interface Builder.
Apple серьезно улучшили Interface Builder в новом Xcode 8. Использование size classes стало более интуитивным, возможность масштабирования сториборда — очень удобной, а полное превью прям в Interface Builder — просто великолепным. Для тех у кого были сомнения насчет использования Interface Builder, это может стать хорошими плюсами.
С другой стороны, у многих разработчиков все еще есть некоторые проблемы с Interface Builder когда они создают большие многоэкранные приложения со сложной навигацией.
В этой статье я поделюсь некоторыми из лучших практик для работы со сторибордами в вашем проекте. Вы уже пользуетесь Interface Builder, или только делаете первые шаги в этом направлении? — в любом случае, эти советы будут полезны для вас.
В вашем проекте есть один файл main.storyboard, который выглядит вот так?
С точки зрения дизайнера, все хорошо: полностью видно UI и навигацию. И это именно то, для чего Interface Builder и был создан.
Но для разработчика это несет множество проблем:
Как же связать различные сториборды в вашем проекте? Есть два способа.
Используйте ссылки на сториборды (storyboard referencing), которые появились в Xcode 7.
О первом способе вы можете почитать детальнее здесь [3].
Я расскажу о втором способе, так как он широко используется для сложных проектов.
Это упростит правила именования, а также даст некоторые "плюшки" о которых поговорим в пункте 3.
Когда дело доходит до инициализации вью-контроллера через сториборд, я часто вижу следующий код:
let storyboard = UIStoryboard(name: “Main”, bundle: nil)
let homeViewController = storyboard.instantiateViewController(withIdentifier: “HomeViewController”)
Немного "грязновато": вам нужно назвать сториборд, вам нужен storyboard ID вью-контроллера, и вам необходимо использовать этот паттерн каждый раз, когда вы создаете HomeViewController.
Лучше перенести этот код в сам класс контроллера и использовать статический метод, чтоб инициализировать контроллер с помощью сториборда:
class HomeViewController: UIViewController {
static func storyboardInstance() -> HomeViewController? {
let storyboard = UIStoryboard(name: “HomeViewController”, bundle: nil)
return storyboard.instantiateInitialViewController() as? HomeViewController
}
}
Если вы последуете предыдущему совету (одинаковые имена файлов), то можете избежать "харкода" имени сториборда и воспользоваться String(describing:):
let storyboard = UIStoryboard(name: String(describing: self), bundle: nil)
Убедитесь, что у файла сториборда такое же имя как и у класса контроллера. Иначе ваше приложение будет «крэшится» когда вы попытаетесь создать ссылку на такой сториборд.
Это делает ваш код более читаемым и отказоустойчивым:
class HomeViewController: UIViewController {
static func storyboardInstance() -> HomeViewController? {
let storyboard = UIStoryboard(name: String(describing: self), bundle: nil)
return storyboard.instantiateInitialViewController() as? HomeViewController
}
}
Если вы хотите иметь доступ к вью-контроллеру через instantiateInitialViewController() убедитесь, что вы указали этот вью-контроллер как initialViewController в Interface Builder. Если у вас несколько вью-контроллеров на одном сториборде, вам придется использовать instantiateViewController(withIdentifier: _ )
Теперь, инициализация такого вью-контроллера займет одну строку:
let homeViewController = HomeViewController.storyboardInstance()
Просто и понятно, не так ли?
Вы можете использовать этот же подход для инициализации вью из nib:
class LoginView: UIView {
static func nibInstance() -> LoginView? {
let nib = Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)
return nib?.first as? LoginView
}
}
У вас не будет переходов, если вы последуете совету из пункта 1. Но даже если у вас есть несколько вью-контроллеров в одном сториборде, использование переходов (segues) для навигации между ними — не очень хорошая идея:
Какова альтернатива? Когда вы хотите перейти к следующему вью-контроллеру по нажатию на кнопку, просто добавьте IBAction для этой кнопки и инициализируйте вью-контроллер в коде: это ведь всего одна строка, как вы помните из пункта 3.
@IBAction func didTapHomeButton(_ sender: AnyObject) {
if let nextViewController = NextViewController.storyboardInstance() {
// initialize all your class properties
// nextViewController.property1 = …
// nextViewController.property2 = …
// either push or present the nextViewController,
// depending on your navigation structure
// present(nextViewController, animated: true, completion: nil)
// or push
navigationController?.pushViewController(nextViewController, animated: true)
}
}
Иногда навигация предполагает возврат пользователя к предыдущему экрану.
Очень распространенная ошибка: использовать новый переход для навигации к предыдущему вью-контроллеру. Такой переход создает новый экземпляр вью-контроллера, который уже находится в стэке, вместо того, чтоб убрать текущий вью-контроллер и таким образом вернуться к предыдущему.
Начиная с iOS 7, Interface Builder дает вам возможность сделать "unwind" навигационного стэка.
Unwind segue [4] позволяет вам указать возврат на предыдущий экран. Это звучит довольно просто, но на практике это требует некоторых дополнительных действий и только сбивает с толку разработчика:
Каков же более простой способ?
Проще делать это в коде: вместо создания действия "unwind" для вашей кнопки, создайте обычный IBAction и используйте dismissViewController или popViewController (в зависимости от вашей навигации):
@IBAction func didTapBackButton(_ sender: AnyObject) {
// if you use navigation controller, just pop ViewController:
if let nvc = navigationController {
nvc.popViewController(animated: true)
} else {
// otherwise, dismiss it
dismiss(animated: true, completion: nil)
}
}
На сегодня это все. Я надеюсь, вы найдете что-то полезное для себя.
Благодаря методу описанному в этой статье, я очень сильно упростил работу со сторибордами в своем текущем проекте. Пока я работал над ним один — все было прекрасно, но как только появились другие разработчики — работа со сторибордом превратилась в настоящий ад. От отчаянья мы практически перешли к "банановому методу" (можно почитать здесь [5] в разделе "Pass the banana").
Конечно же, в идеале нужно будет рано или поздно прийти к VIPER [6]. Но об этом будет уже другой пост. :)
Автор: s_suhanov
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/xcode/200042
Ссылки в тексте:
[1] Xcode: A Better Way to Deal with Storyboards: https://medium.com/ios-os-x-development/xcode-a-better-way-to-deal-with-storyboards-8b6a8b504c06
[2] Stan Ostrovskiy: https://medium.com/@stasost
[3] здесь: http://useyourloaf.com/blog/refactoring-with-storyboard-references/
[4] Unwind segue: https://developer.apple.com/library/content/technotes/tn2298/_index.html
[5] здесь: https://spin.atomicobject.com/2014/02/18/ios-storyboards-xcode5/
[6] VIPER: https://habrahabr.ru/post/273061/
[7] Источник: https://habrahabr.ru/post/312766/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.