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

时间:2016-09-19 15:55:37

标签: ios swift protocols swift3 swift-protocols

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

  • 简明:最小化从XCode到某些关键信息的自动完成功能。我想以简单的方式知道在将控制器推到堆栈之前所需的确切参数。

  • 避免segues 。根据我的理解,segues在故事板中创建了很多紧密耦合。我不想依赖故事板来传递信息。 (每次我想将控制器A切换到另一个时,我都要去故事板进行一些更改)。我可能会在某些时候在几个故事板中拆分我的应用程序,并且segues可能会非常讨厌处理。

  • 美丽:也许Swift可以提供我没想过的Swifty解决方案。

我最初想要完成的是将控制器作为协议推送。即使不可能,让我解释一下:

  • 将控制器作为协议推送,可以让我对我的属性有确切的可见性。

  • 没有与故事板相关的紧密耦合

  • 许多控制器(A,C,D)可能想要推送B控制器,我可能会给他们每个人一个不同的协议来推动控制器B.也许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)。你的想法是什么?为什么我不想这样做?

2 个答案:

答案 0 :(得分:2)

我会从另一侧(即你正在呈现的控制器的一侧)接近这个。

您可以执行类似创建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需要什么以及如何以及在何处呈现它并用数据填充它。各个视图控制器实际上并不需要了解彼此的任何信息。

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

此外,不要太担心创造“正常工作”的东西,即使它不是教科书通用,“Swifty”,优雅,漂亮的代码。拥有有效的代码要比设计精美的系统好得多。

:)

答案 1 :(得分:1)

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

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

<强>优点

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

<强>缺点

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

这是我班上当前的实现:

在控制器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?

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