在情节提要中使用 segues 使视图控制器具有非可选属性?

Posted

技术标签:

【中文标题】在情节提要中使用 segues 使视图控制器具有非可选属性?【英文标题】:Using segues in storyboard make a view controller that has non optional properties? 【发布时间】: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 值之前尝试使用它会崩溃。 【参考方案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,我可以在继续之前设置值。

【讨论】:

以上是关于在情节提要中使用 segues 使视图控制器具有非可选属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何使 Xcode segues 不可逆?

Segues 直接从情节提要 xcode 中的视图控制器警告启动

在使用情节提要和 segue 时打开它之前选择 tabbarcontroller 的默认选项卡

使用 Swift 在没有情节提要的情况下展开 segue

情节提要弹出框已关闭,未调用委托方法

如何摆脱或更改情节提要以编程方式创建 segue