以 Swifty(/协议)方式在控制器之间传递信息?

Passing information between controllers in a Swifty (/protocol) way?

我正在尝试将信息从控制器 A 传递到控制器 B。问题是,我想要:

一开始我一直在努力完成的是将控制器作为协议推送。即使不可能,让我解释一下:

起初,我的代码看起来像那样(故事板扩展有效):

if let myBCustomVC = storyboard.instanciateControllerWithIdentifier(MyBCustomVC.self) as? myVCustomFromAProtocol{
    myBCustomVC.AToB = self.data.count
    self.navigationController?.pushViewController(myBCustomVC, animated: true)
}

protocol myVCustomFromAProtocol {
    var AToB: Int {get set}
}

问题是,我无法将视图控制器向下推送到协议。我不得不通过一个丑陋的 UINavigationController 扩展名来。这是完整的结果。

if let myBCustomVC = storyboard.instanciateControllerWithIdentifier(MyBCustomVC.self) as? myVCustomFromAProtocol{
    myBCustomVC.AToB = self.data.count
    self.navigationController?.pushViewController(vcToPush: myBCustomVC, animated: true)
}

extension UINavigationController{
    func pushViewController(vcToPush: Any, animated: Bool){
        if let vc = vcToPush as? UIViewController{
            self.pushViewController(vc, animated: animated)
        }
    } 
}

让我们面对现实吧,在实现我的前两个目标的同时,我已经将 Any 向下转换为 UIViewController,呜呜。

有什么方法可以避免紧密耦合并以漂亮的方式将控制器推入堆栈,同时保持有限可见性 从第一个视图控制器 (A) 传递到第二个 (B) 的参数。你都有些什么想法呢?为什么我不想那样做?

我会从另一侧(即您展示的控制器的一侧)来处理这个问题。

您可以创建一个 Presenter 协议。

protocol Presenter {
    func present(inContext context: UIViewController)
    // possibly with some data in it too (if it's across all instances)...
    var count: Int {get set}
}

并且在每个之上都有某种工厂结构...

struct BVCPresenter: Presenter {
    var count: Int = 0

    func present(inContext context: UIViewController) {
        // create the BViewController here
        let controller = ...

        context.push(controller, animated: true)
    }
}

或者类似的东西。这实际上取决于用例和传递的数据等...

所以现在 A 可以做...

someGenericPresenter.present(inContext: navigationController)

通用表示器可以作为 属性 传递给 A,也可以作为参数传递给函数等...

什么的。

您甚至可以创建一个 "FlowController" 来为您管理这些转换和数据...

self.flowController.presentB()

然后 flowController 知道 B 需要什么以及如何以及在何处呈现它并用数据填充它。各个视图控制器实际上不需要了解彼此。

我认为没有适合所有情况的解决方案。这需要大量的思考和设计。但是有很多选择。想想你在视图控制器之间需要什么,然后从中开始工作。

另外,不要太担心创建 "just works" 的东西,即使它不是教科书中的通用代码,"Swifty",优雅,漂亮的代码。拥有可以工作的代码比拥有一个设计精美但不能工作的系统要好得多。

:)

在 Fogmeister 的帮助下,我得出了这个答案。与使用从协议继承的 Presenter 对象相比,它有几个优点和缺点。

解决方案:使用位于要实例化的控制器中的静态工厂方法(或几个取决于用例)。

优点:

  • 使用静态工厂方法强制传递实际控制器知道他自己的初始化所需的参数。
  • 不涉及结构(复杂度较低?)
  • 随着时间的推移,不断使用静态工厂方法变得很自然。您知道在查看控制器时必须实现调用它的方法,并且您知道所需的参数。您不必担心必须实现哪些参数才能推送控制器,只需实现功能即可。使用演示器时,您没有这方面的知识,因为展开的参数可能很多。
  • Presenter的使用更接近MVP,脱离了Apple(MVC)提倡的依赖注入模型

缺点:

  • Fogmeister 建议的 Presenter 模式的使用使用了一个协议。该协议为控制器 A(以及所有想要推送控制器 B 的控制器)提供了有限的可见性。
  • Presenter 模式的使用提供了更多的模块化并减少了紧耦合。控制器 A 不需要知道控制器 B 的任何信息。将控制器 B 替换为另一个控制器 C 可以在不影响所有要转到 B 的控制器的情况下完成(如果您正在重构应用程序或在某些用例中,这将非常有用).

这是我当前在 class:

中的实现

在控制器 B(静态函数)中:

static func prepareController(originController: UIViewController, AToB: Int) -> bVC? {
    if let bVC = originController.storyboard?.instanciateControllerWithIdentifier(self) as? bVC{
        bVC.AToB = AToB
        return bVC
    }
    else{
        return nil
    }
}

在控制器A中(将控制器压入堆栈):

 if let bVC = bVC(originController: self, AToB: *someValue*){
     self.navigationController?.pushViewController(bVC, animated: true)
 }

必须对解决方案持保留态度,让我知道您的想法@Fogmeister?

两种解决方案都可以结合使用。使用 协议结构 将使用控制器提供的静态方法。问题是,对于您介绍给项目的人来说,它很快就会变得过于复杂。这就是为什么我现在坚持我的解决方案。不过,我正在研究依赖注入框架。