1

假设我们有一个UITableViewControllerthat ondidSelectRowAtSection加载一个名为 ie: 的类的实例,ClassToInject并且它想通过属性注入来注入它,因为我们ViewControllerToBePushed有一个 , 的属性ClassToInject,随后(因为它是一个UITabBarViewController)在didSet回调中搜索它的所有viewControllers属性符合ClassToInjectPresentable简单的:

protocol ClassToInjectPresentable { 
  var property: ClassToInject { get set } 
}

到现在为止,我只会做这样的事情:

func didSelectRowAtIndexPath {
     let classToInject = self.loadClassToInjectFor(indexPath)
     let tabBarViewController = SomeTabBarViewController()
     tabBarViewController.property = classToInject
     self.navigationController.push(tabBarViewController, animated: true)
}

而在SomeTabBarViewController...

class SomeTabBarViewController: ClassToInjectPresentable {
  var property: ClassToInject? {
  didSet(newValue) {
      self.viewControllers.filter{ $0 is ClassToInjectPresentable }.map{ $0 as! ClassToInjectPresentable }.forEach{ $0.property = newValue }
  }
 }

并且所有内容都应该轻松加载(但事实并非如此)。我读过Swinject这可能会解决它。我已经看到很多注册的例子,比如:

container.register(Animal.self) { _ in Cat(name: "Mimi") }

但我不知道我是否可以注册一些加载的属性self

container.register(ClassToInjectInjector.self) { _ in 
self.loadClassToInjectFor(indexPath) }
// And then
container.register(ClassToInjectPresentable.self) { _ in 
SomeTabBarViewController() }
    .initCompleted { r, p in
        let tabBar = p as! SomeTabBarViewController
        tabBar.property = r.resolve(ClassToInjectInjector.self)
        // And lastly?
        self.navigationController.pushViewController(tabBar, animated: true)
    }
}
4

2 回答 2

2

在不了解您的应用程序细节的情况下很难推荐合适的解决方案,但这里有一些建议:

container.register(ClassToInjectInjector.self) { _ in 
    self.loadClassToInjectFor(indexPath) 
}

一般来说,所有register的动作都应该在你的对象之外完成。常见的设置是拥有一个包含所有注册的 global Container——您应该将它们视为构建应用程序对象的指令,而无需任何隐式上下文。如果需要在 中创建依赖项UITableViewController,可以将其resolve作为参数传递给方法:

container.register(ClassToInjectPresentable.self) { resolver, property in
    let tabBar = SomeTabBarViewController()
    tabBar.property = property
    return tabBar
}

// in UItableVIewController
container.resolve(ClassToInjectPresentable.self, 
                  argument: self.loadClassToInjectFor(indexPath))

这通常也是一个坏主意:

.initCompleted { r, p in
    ...
    self.navigationController.pushViewController(tabBar, animated: true)
 }

您不应该将应用程序逻辑与 DI 混合使用 - 纯粹使用 Swinject 来构建您的依赖项。

所以你UITableViewController可能看起来像这样:

func didSelectRowAtIndexPath {
    let classToInject = self.loadClassToInjectFor(indexPath)
    let tabBar = container.resolve(
        SomeTabBarViewController.self, argument: loadClassToInjectFor(indexPath)
    )
    navigationController.push(tabBar, animated: true)
}

至于您TabBar及其视图控制器:如何UIViewControllers进入TabBar?有可能做这样的事情吗?

class SomeTabBarViewController {
    init(viewControllers: [UIViewController]) {
        ...
    }   
}

container.register(SomeTabBarViewController.self) { r, property
    SomeTabBarViewController(viewControllers:[
        r.resolve(MyViewController.self, argument: property),
        r.resolve(MyViewController2.self, argument: property)
    ])
}
于 2017-06-06T08:14:30.480 回答
1

最后我按照提出的建议得到了最终的答案。

public class Containers {
    fileprivate init() { }
}

extension Containers {
    static let activityPresentableContainer: Container = {
        let container = Container()
        container.register(ActivityTabBarController.self) { (r: Resolver, arg1: Activity) in
            return ActivityTabBarController(activity: arg1)
        }
        container.register(ActivityPresentable.self) {
           (r: Resolver, arg1: ActivityPresentableTabs, arg2: Activity) in
           switch arg1 {
           case .summary:
               return ActivitySummaryViewController(activity: arg2)
           case .detail:
               return ActivityDetailPageViewController(activity: arg2)
           case .map:
               return ActivityMapViewController(activity: arg2)
           case .charts:
               return ActivityChartsViewController(activity: arg2)
           case .strava:
              return ActivityStravaViewController(activity: arg2)
           }
        }.inObjectScope(.transient)
       return container
    }()

使用这种方法,命名ActivityTabBarController总是通过activityPresentableContainer使用以下语句来实例化:

let controller = Containers.activityPresentableContainer.resolve(
    ActivityTabBarController.self, argument: activity
)!

然后,TabBarController 中的每个选项卡都使用所需的参数Activity和选项卡本身的类型使用.transient上下文进行实例化。它像这样解决:

let activitySummary = Containers.activityPresentableContainer.resolve(
   ActivityPresentable.self, arguments: ActivityPresentableTabs.summary, activity!
) as! UIViewController

通过这种方式,我可以仅根据它们使用的信息来概括标签栏的标签。如果其中一个选项卡随时更改,我可以按照ActivityPresentable协议更改注册。

于 2017-06-06T13:41:56.467 回答