Редкий разработчик под iOS или OS X не использовал сториборды и еще меньше программистов не передавали данные между экранами.
Все мы знаем метод performSegueWithIdentifier и трудности работы с ним.
Начав проект на Swift в один момент мне стало обидно: «Почему мы должны в строго-типизированном языке использовать обертку для передачи данных?»
Через пару минут сформировалось видение решения и в скором времени реализация.
Долго думал, стоит ли писать об этом, поскольку материал крайне небольшой, но эти 50 строк могут вам очень сильно помочь
Таким образом, легко использовать сториборды и роутеры вместе. Устранить разрастание prepareForSegue и увеличить контекст чтения кода. В то же время это не вынуждает отказываться от prepareForSegue при удобных ситуациях.
Разберемся как это работает? Не претендую на уникальность, но мне кажется подход интересным.
1) Создаем метод аналогичный performSegueWithIdentifier, но с дополнительным параметром closure (configurate)
2) Сохраняем configurate
3) Вызываем метод performSegueWithIdentifier
4) В момент вызова prepareForSegue осталось только вызвать configurate
Можно написать класс, однако намного продуктивнее написать категорию.
Тонким моментом является сохранение configurate и вызов его из prepareForSegue (который важно не сломать своими действиями).
Для сохранения будем использовать ассоциативные ссылки. Вместо prepareForSegue будет вызываться наш метод, откуда будет вызываться configurate и уже потом вызываться оригинальный метод.
Реализация очень небольшая, и проще приложить код под спойлер
На этом все. Это очень маленький хак, который работает и на objc, и на Swift.
Это не только не нарушает написанный вами код ранее, но и позволяет комбинировать оба подхода или же полностью перейти на конфигурирование из блоков.
Так же это позволяет вынести зависимость переходов между экранами в специальный Router класс и использовать вместе со сторибордами
Все мы знаем метод performSegueWithIdentifier и трудности работы с ним.
Начав проект на Swift в один момент мне стало обидно: «Почему мы должны в строго-типизированном языке использовать обертку для передачи данных?»
Через пару минут сформировалось видение решения и в скором времени реализация.
Долго думал, стоит ли писать об этом, поскольку материал крайне небольшой, но эти 50 строк могут вам очень сильно помочь
menuController?.performSegueWithIdentifier(changeItemIdentifier, sender: nil) { segue in
let controller = segue.destinationViewController as! ChangeMenuItemController
controller.viewModel.sourceMenuItem = item
}
Таким образом, легко использовать сториборды и роутеры вместе. Устранить разрастание prepareForSegue и увеличить контекст чтения кода. В то же время это не вынуждает отказываться от prepareForSegue при удобных ситуациях.
Разберемся как это работает? Не претендую на уникальность, но мне кажется подход интересным.
1) Создаем метод аналогичный performSegueWithIdentifier, но с дополнительным параметром closure (configurate)
2) Сохраняем configurate
3) Вызываем метод performSegueWithIdentifier
4) В момент вызова prepareForSegue осталось только вызвать configurate
Можно написать класс, однако намного продуктивнее написать категорию.
Тонким моментом является сохранение configurate и вызов его из prepareForSegue (который важно не сломать своими действиями).
Для сохранения будем использовать ассоциативные ссылки. Вместо prepareForSegue будет вызываться наш метод, откуда будет вызываться configurate и уже потом вызываться оригинальный метод.
Верхнеуровневый код
typealias ConfiguratePerformSegue = (UIStoryboardSegue) -> ()
func performSegueWithIdentifier(identifier: String, sender: AnyObject?, configurate: ConfiguratePerformSegue?) {
swizzlingPrepareForSegue()
configuratePerformSegue = configurate
performSegueWithIdentifier(identifier, sender: sender)
}
Реализация очень небольшая, и проще приложить код под спойлер
Реализация
class Box {
let value: Any
init(_ value: Any) {
self.value = value
}
}
extension UIViewController {
struct AssociatedKey {
static var ClosurePrepareForSegueKey = "ClosurePrepareForSegueKey"
static var token: dispatch_once_t = 0
}
typealias ConfiguratePerformSegue = (UIStoryboardSegue) -> ()
func performSegueWithIdentifier(identifier: String, sender: AnyObject?, configurate: ConfiguratePerformSegue?) {
swizzlingPrepareForSegue()
configuratePerformSegue = configurate
performSegueWithIdentifier(identifier, sender: sender)
}
private func swizzlingPrepareForSegue() {
dispatch_once(&AssociatedKey.token) {
let originalSelector = #selector(UIViewController.prepareForSegue(_:sender:))
let swizzledSelector = #selector(UIViewController.closurePrepareForSegue(_:sender:))
let instanceClass = UIViewController.self
let originalMethod = class_getInstanceMethod(instanceClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(instanceClass, swizzledSelector)
let didAddMethod = class_addMethod(instanceClass, originalSelector,
method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(instanceClass, swizzledSelector,
method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
func closurePrepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
configuratePerformSegue?(segue)
closurePrepareForSegue(segue, sender: sender)
configuratePerformSegue = nil
}
var configuratePerformSegue: ConfiguratePerformSegue? {
get {
let box = objc_getAssociatedObject(self, &AssociatedKey.ClosurePrepareForSegueKey) as? Box
return box?.value as? ConfiguratePerformSegue
}
set {
objc_setAssociatedObject(self, &AssociatedKey.ClosurePrepareForSegueKey, Box(newValue), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
}
На этом все. Это очень маленький хак, который работает и на objc, и на Swift.
Это не только не нарушает написанный вами код ранее, но и позволяет комбинировать оба подхода или же полностью перейти на конфигурирование из блоков.
Так же это позволяет вынести зависимость переходов между экранами в специальный Router класс и использовать вместе со сторибордами
Поделиться с друзьями
Комментарии (4)
akkrat
31.05.2016 08:57Подобное (и даже больше) уже реализовано в VIPER McFlurry github.com/rambler-ios/ViperMcFlurry
ajjnix
31.05.2016 08:57+1Отлично, как я и написал «Не претендую на уникальность».
В конечном итоге люди узнали/узнают что «можно так», а есть «и вот так уже». Я вот не знал когда делал, думаю я не один такой :-)
fiveze
Красивая реализация. Спасибо!