Шаблон координатора - Использование раскадровки вместо Xib - PullRequest
0 голосов
/ 17 мая 2018

Я впервые использую шаблон координатора . Хотя я понял, что это важно, но у меня есть одна серьезная проблема.
Я прошел эту удивительную статью по этому шаблону. На самом деле я смог создать демонстрационный проект самостоятельно, используя это. Однако есть один момент - предлагается использовать Xib . Не упоминается исключительно, что Раскадровки не могут быть использованы, но, пройдя эти строки ближе к концу статьи, я думаю иначе:

С великой силой приходит большая ответственность (и ограничения). Использовать это расширение, вам нужно создать отдельную раскадровку для каждого UIViewController. Название раскадровки должно соответствовать названию класс UIViewController. Этот UIViewController должен быть установлен как начальный UIViewController для этой раскадровки.

Следует упомянуть, что в случае Раскадровки мы должны создать расширение и использовать его в UIViewController:

extension MyViewController: StoryboardInstantiable {
}  

StoryboardInstantiable:

import UIKit

protocol StoryboardInstantiable: NSObjectProtocol {
  associatedtype MyType  // 1
  static var defaultFileName: String { get }  // 2
  static func instantiateViewController(_ bundle: Bundle?) -> MyType // 3
}

extension StoryboardInstantiable where Self: UIViewController {
  static var defaultFileName: String {
    return NSStringFromClass(Self.self).components(separatedBy: ".").last!
  }

  static func instantiateViewController(_ bundle: Bundle? = nil) -> Self {
    let fileName = defaultFileName
    let sb = UIStoryboard(name: fileName, bundle: bundle)
    return sb.instantiateInitialViewController() as! Self
  }
}

Запросы:

  1. Как автор упомянул, что для каждого UIViewController необходимо создать отдельную раскадровку , как лучше использовать Xib в шаблоне координат ?
  2. Зачем нам нужно создавать отдельную раскадровку для каждого UIViewController? Разве мы не можем использовать для этого идентификатор раскадровки UIViewController, не связывая какие-либо UIViewController с использованием сегментов? Таким образом можно настроить вышеуказанное расширение с помощью идентификатора и легко добиться того же.

Ответы [ 4 ]

0 голосов
/ 20 февраля 2019

Я использовал enum и изменил метод instanciate ().У меня все отлично работает

enum OurStoryboards: String{
    case MainPage = "MainPage"
    case Catalog = "Catalog"
    case Search = "Search"
    case Info = "Info"
    case Cart = "Cart"
}

protocol Storyboarded {
    static func instantiate(_ storyboardId: OurStoryboards) -> Self
}

extension Storyboarded where Self: UIViewController {
    static func instantiate(_ storyboardId: OurStoryboards) -> Self {

        let id = String(describing: self)
        // load our storyboard
        var storyboard = UIStoryboard()
        switch storyboardId {
        case .MainPage:
            storyboard = UIStoryboard(name: OurStoryboards.MainPage.rawValue ,bundle: Bundle.main)
        case .Catalog:
            storyboard = UIStoryboard(name: OurStoryboards.Catalog.rawValue ,bundle: Bundle.main)
        case .Search:
            storyboard = UIStoryboard(name: OurStoryboards.Search.rawValue ,bundle: Bundle.main)
        case .Info:
            storyboard = UIStoryboard(name: OurStoryboards.Info.rawValue ,bundle: Bundle.main)
        case .Cart:
            storyboard = UIStoryboard(name: OurStoryboards.Cart.rawValue ,bundle: Bundle.main)
        }
        // instantiate a view controller with that identifier, and force cast as the type that was requested
        return storyboard.instantiateViewController(withIdentifier: id) as! Self
    }

}
0 голосов
/ 26 мая 2018

Мой способ использования Координаторов с раскадровками - использование нескольких раскадровок.Одна раскадровка для каждой функции / модуля.

Почему несколько раскадровок вместо одной?При работе с большим количеством функций и в команде лучше разделить вашу раскадровку, потому что использование только одной раскадровки приведет к большому количеству конфликтов слияния , а исправление конфликтов git раскадровки - одна из трудностей того, чтобыРазработчик iOS.

Вот как я это делаю.

Сначала у меня есть протокол под названием AppStoryboardType, который я буду реализовывать в перечислении, содержащем имена всех моих раскадровок.

protocol AppStoryboardType {
    var instance: UIStoryboard { get }

    func instantiate<T: UIViewController>(_ viewController: T.Type, function: String, line: Int, file: String) -> T

    func instantiateInitialViewController() -> UIViewController?
}

extension AppStoryboardType {
    func instantiateInitialViewController() -> UIViewController? {
        return self.instance.instantiateInitialViewController()
    }
}

extension AppStoryboardType where Self: RawRepresentable, Self.RawValue == String {
    var instance: UIStoryboard {
        return UIStoryboard(name: self.rawValue, bundle: nil)
    }

    func instantiate<T: UIViewController>(
        _ viewController: T.Type,
        function: String = #function,
        line: Int = #line,
        file: String = #file) -> T {

        let storyboardID: String = T.storyboardIdentifier

        guard let vc = self.instance.instantiateViewController(withIdentifier: storyboardID) as? T else {
            fatalError("ViewController with identifier \(storyboardID), not found in \(self.rawValue) Storyboard.\nFile : \(file) \nLine Number : \(line) \nFunction : \(function)")
        }

        return vc
    }
}

enum AppStoryboard: String, AppStoryboardType {
    case Main /* ... Insert your other storyboards here. */

    // These are the refactored modules that use coordinator pattern.
    case PasswordRecovery, Registration
}

extension UIViewController {
    public static var defaultNibName: String {
        return self.description().components(separatedBy: ".").dropFirst().joined(separator: ".")
    }

    static var storyboardIdentifier: String {
        return "\(self)"
    }

    static func instantiate(fromAppStoryboard appStoryboard: AppStoryboard) -> Self {
        return appStoryboard.instantiate(self)
    }
}

Теперь, когда я показал вам базу, которую я использую, вот как она реализована в коде.

let viewController = AppStoryboard.Login.instantiate(LoginViewController.self)
viewController./// set properties if ever you need to set them
presenter.present(viewController, animated: true, completion: nil)

PS: Большую часть времени для каждого модуля / функции у меня есть свои Раскадровка и Координатор , но это зависит от возможности многократного использования UIViewController, который вы будете использовать.

РЕДАКТИРОВАТЬ :

1 год спустя я перестал использовать свой подход AppStoryboard и теперь вместо него использую библиотеку Reusable .Причина этого в том, что он чище и менее подвержен человеческим ошибкам.

Теперь вместо того, чтобы программист (мы) узнал, к какой раскадровке подключен конкретный VC, теперь мы можем просто подклассировать контроллер представления вStoryboardSceneBased, предоставьте раскадровку этого контроллера вида и создайте его экземпляр, выполнив CustomViewController.instantiate()

// From this code
let viewController = AppStoryboard.Login.instantiate(LoginViewController.self)

// To this code
let viewController = LoginViewController.instantiate()
0 голосов
/ 06 января 2019

Важно понимать, что координатор управляет потоком приложения и логика может быть написана так, как вам нравится.Однако, если вы напишите остальную логику, следуя лучшим практикам, вы поймете, почему это сделано именно так и что это вам дает.

Вся логика взаимодействия координаторов и другихКомпоненты архитектуры могут быть описаны по следующей схеме.

Application coordinator scheme

Пример реализации экрана авторизации (Router,Фабрика координаторов, Фабрика потоков, Базовый координатор, Детский координатор)

Пример реализации архитектуры (SOA / Application Coordinator / VIPER)

Посмотрите на этот проект.Я уверен, что вам понравится.Я надеюсь, что смогу помочь вам понять эту замечательную тему координаторов.

0 голосов
/ 17 мая 2018

Я много раз читал это руководство, и оно использует координатор для каждого контроллера View, что не имеет смысла для меня. Я думал, что цель Координатора состояла в том, чтобы переместить логику навигации от контроллеров представления к объекту более высокого уровня, который может управлять общим потоком.

Если вы хотите инициализировать ViewControllers из основной раскадровки, используйте вместо этого этот протокол и расширение:

import UIKit

protocol Storyboarded {
    static func instantiate() -> Self
}

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        // this pulls out "MyApp.MyViewController"
        let fullName = NSStringFromClass(self)

        // this splits by the dot and uses everything after, giving "MyViewController"
        let className = fullName.components(separatedBy: ".")[1]

        // load our storyboard
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

        // instantiate a view controller with that identifier, and force cast as the type that was requested
        return storyboard.instantiateViewController(withIdentifier: className) as! Self
    }
}

Единственное требование заключается в том, чтобы каждый используемый им контроллер View имел этот протокол и имел StoryboardID с тем же именем, что и у класса.

Вы можете использовать это следующим образом:

private func startBlueFlow() {
    let vc = BlueViewControllerOne.instantiate()
    vc.coordinator = self
    self.navigationController.push(vc, animated: true)
}

Отказ от ответственности: Протокол взят из этой статьи , которая также может помочь вам

ОБНОВЛЕНИЕ: (добавлена ​​ссылка)

Soroush Khanlou обычно упоминается и упоминается в других статьях и руководствах по шаблону координаторов в iOS и Redux. У него есть статья здесь (от 2015 года, код в target-c), которая может быть вам интересна.

...