【问题标题】:Passing information between controllers in a Swifty (/protocol) way?以 Swifty (/protocol) 方式在控制器之间传递信息?
【发布时间】:2016-09-19 15:55:37
【问题描述】:

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

  • 简洁:尽量减少从XCode 到一些关键信息的自动完成。在将控制器推入堆栈之前,我想以一种简单的方式了解所需的确切参数。

  • 避免转场。据我了解,segue 在故事板中创建了很多紧密耦合。我不想依赖故事板来传递信息。 (每次我想将控制器 A 切换到另一个时,我都必须去情节提要进行一些更改)。我可能会在某个时候将我的应用拆分为多个故事板,处理转场可能会很烦人。

  • Beautiful:也许 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)。你怎么认为?为什么我不想这样做?

【问题讨论】:

标签: ios swift protocols swift3 swift-protocols


【解决方案1】:

我会从另一侧(即您正在展示的控制器的一侧)接近它。

你可以做一些事情,比如创建一个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)

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

什么的。

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

self.flowController.presentB()

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

我认为没有适合所有情况的解决方案。它需要大量的思考和设计。但是有很多选择。想想你在视图控制器之间需要什么,并以此为基础工作。

另外,即使不是教科书通用、“Swifty”、优雅、漂亮的代码,也不要太担心创建“正常工作”的东西。拥有可以运行的代码比拥有一个设计精美但无法运行的系统要好得多。

:)

【讨论】:

  • 感谢您的快速回答和好主意。我会努力实施并尽快回复您。
  • @SwiftSun 祝你好运。不要将我的回答作为具体的解决方案,而是围绕不同的想法工作。也请查看评论中链接到您的 Q 的视频 :) 还有一些不错的 WWDC 视频可供浏览。
  • 根据您的回答,一些同事建议在我的控制器 B 中执行静态方法(从演示者的角度来看)。静态方法只需要一个 UINavigationController 和参数。这样我可以有不同的实现,我只能传递静态函数指定的参数。这样,所有的逻辑都在 B 控制器中,而 A 不必担心实现。大加:不需要协议也不需要结构;强制传递参数,协议强制变量不传递信息。你怎么看?
  • @SwiftSun 听起来很合理。这与我认为的可能选项类似。这仍然意味着调用它的控制器必须知道呈现每个控制器所需的参数。您可以很容易地将该依赖项转移到另一个对象中。
  • 谢谢,当我有时间时,我会发布静态解决方案作为答案。我仍然认为您的解决方案可以呈现某些优势,例如受限的可见性和更多的模块化。感谢您的帮助!
【解决方案2】:

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

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

优势

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

缺点

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

这是我目前在课堂上的实现:

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

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

【讨论】:

    猜你喜欢
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-05
    • 2017-03-29
    • 1970-01-01
    • 2016-02-11
    相关资源
    最近更新 更多