【问题标题】:Using segues in storyboard make a view controller that has non optional properties?在情节提要中使用 segues 使视图控制器具有非可选属性?
【发布时间】:2017-03-07 18:24:34
【问题描述】:

我有一个视图控制器,它有一个属性。

class GameVC: UIViewController {

    var game:Game?
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

要使视图控制器有意义,它必须具有游戏属性。出于这个原因,我希望该属性不是可选的(我也不想在整个使用过程中都不必打开它)。

我目前在此之前在 VC 中覆盖为 segue 做准备并设置游戏属性。这是因为我绝对想使用 segues 和故事板。无论如何我可以让这个视图控制器有一个自定义的 init 并且仍然使用 segues 和 storyboards?

【问题讨论】:

  • 您不能使用自定义初始化,但您可以将属性设置为隐式展开的可选:var game: Game!。这样你在使用它之前就不必打开它,但是如果你在它被设置为非 nil 值之前尝试使用它会崩溃。

标签: ios swift uiviewcontroller segue


【解决方案1】:

是的。你有两个选择:

在声明中初始化:

class GameVC: UIViewController {
     let game = Game()

实现init(coder:)awakeFromNib

class GameVC: UIViewController {
     let game : Game
     required init?(coder aDecoder: NSCoder) {
         self.game = Game()
         super.init(coder:aDecoder)
     }

【讨论】:

    【解决方案2】:

    在我看来,在使用情节提要时没有很好的依赖注入方式。我认为故事板违背了面向对象的设计原则。对于团队中的软件开发,我不建议使用它们,除非是原型设计。

    相反,我使用简单的 xib 文件并尝试使屏幕(又名 UIViewControllers)尽可能独立。我还在自己的线框类中实现屏幕之间的导航,以将动画导航与 UIViewController 的主要目的(构建/管理视图树)分开。然后可以注入必要的对象,而不会完全失去使用界面生成器的好处。

    例子:

    final class MyViewController: UIViewController {
    
        convenience override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
            self.init()
        }
    
        convenience required init(coder aDecoder: NSCoder) {
            self.init()
        }
    
        convenience init() {
            fatalError("Not supported!")
        }
    
        init(viewModel: ViewModel, wireframe: Wireframe) {
            self.viewModel = viewModel
            self.wireframe = wireframe
            super.init(nibName: "MyViewController", bundle: Bundle(for: type(of: self)))
        }
    
        private let viewModel: ViewModel
        private let wireframe: Wireframe
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // do something with viewModel
        }
    
        @IBAction func close() {
            wireframe.close()
        }
    
    }
    
    let wireframe: Wireframe = ConcreteWireframe()
    let viewModel: ViewModel = ConcreteViewModel()
    let screen: UIViewController = MyViewController(viewModel: viewModel, wireframe: wireframe)
    

    【讨论】:

      【解决方案3】:

      不,Storyboard(和 XIB 文件)使用 init?(coder:) 来实例化元素,您不能将其他参数传递给此 init。

      通常的做法是使用隐式解包选项:

      var game:Game!
      

      这样,您可以像使用非可选属性一样使用它,如果在使用前未设置,它将在运行时崩溃。

      如果您在prepare(for:sender:) 中设置此属性,它将在viewDidLoad 中准备好,因此可以安全使用。

      【讨论】:

      • 同意,但它必须是可选的,否则编译器会强制您在 init 方法中为其赋值。
      • @DuncanC 是一个隐式解包的可选项,所以不需要在 init 中赋值
      【解决方案4】:

      在我的应用中,我只使用类似的东西

      var game = Game()

      这样它就永远不会是nil,我可以在继续之前设置值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-21
        • 2012-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-19
        • 2014-02-17
        • 2012-07-10
        相关资源
        最近更新 更多