Swift View和Controller共享变量初始化问题

时间:2015-12-15 00:53:44

标签: swift uiview uiviewcontroller initialization swift2

我正在将一些代码从Objc移植到Swift。并与Swift的初始化生命周期挣扎。从简化的角度来看,我试图在这里发挥4个球员:

  1. 一个名为Program的对象。这是我此时的主要顶级模型对象。其余3名玩家都想要访问他的一个实例。
  2. A ProgramEditController,画在我的主Main.storyboard中。他负责实例化一个初始程序,该程序无法直接作为属性初始化程序完成。
  3. 顶级自定义UIView子类,名为ProgramTimelineView。绘制在Main.storyboard中,管理各种专业子视图。链接到我的ViewController的属性。它的子视图也有属性。它想要访问该程序,因此它可以进行布局并将其传递给子视图。
  4. ProgramTimelineView的特定子视图,名为ProgramGridView。这些不是在XCode画布工具中绘制的,而是由包含ProgramTimelineView直接实例化的。它想要访问该程序。使用它来进行自定义drawRect
  5. 以下是我的控制器的相关代码:

    class ProgramEditController: UIViewController { 
        // MARK: - Variables
        @IBOutlet var timelineView:ProgramTimelinesView!
    
        var site = Site()
    
        var program:Program!
    
        // MARK: - Initialize
    
        override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
            super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
            // set up the new program
            self.program = self.site.newProgram() 
            // get it into our top view before it starts drawing
            self.timelineView.program = self.program 
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder) // Why does Swift make me have a redundant thing here?
        }
    
    }
    

    对于ProgramTimelinesView:

    class ProgramTimelinesView: UIView {
        // MARK: - Variables
        var gridView = ProgramGridView()
    
        var program:Program! {
            didSet {
                self.gridView.program = self.program
            }
        }
    
        // MARK: - Initialization
    
        func addGridView() {
            self.gridView.alpha = 0.0
            self.gridView.opaque = false
            self.addSubview(self.gridView)
        }
    
        func commonInit() {
            self.addGridView()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.commonInit()
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.commonInit()
        }
    
    }
    

    最后是ProgramGridView

    class ProgramGridView: TimeAxisView {
        // MARK: - Variables
        var program:Program!
    
        override func drawRect(rect: CGRect) {
            // access self.program in here
        }
    }
    

    我认为会发生什么:

    1. ProgramEditController.init(nibName...)会首先开火。
    2. 超级电话会导致ProgramTimelineView.init(coder)开火。
    3. ProgramTimelineView实例首先会调用gridView初始值设定项,将其设置为新的ProgramGridView视图
    4. ProgramTimelineView.init(coder)的剩余部分将运行,这会将gridView添加到视图树中。
    5. 控件将返回ProgramEditController.init(nibName)初始值设定项。将填充控制器的program属性。
    6. 绑定的timelineView将设置program属性。
    7. ProgramTimelineView会依次设置program的{​​{1}}属性。
    8. 在步骤4和步骤5之间似乎发生的事情是gridView发生了。这会导致seg故障,因为gridView的程序还没有设置好!但是为什么在这一点上发布drawRect()呢?我认为在所有初始化程序都被解雇之前不会发生这种情况。但显然有一些副作用正在发生。使用什么样的正确模式/成语来避免这种情况?我真的不想把所有的drawRect()变成program!,然后把let / guard放在每个地方。

1 个答案:

答案 0 :(得分:0)

在我原来的场所(通常是这种情况),结果证明是错误的假设。

从界面构建器资产创建时,不会调用

from sklearn import datasets import pandas as pd tmp = datasets.load_iris() iris = pd.DataFrame(tmp.data, columns=tmp.feature_names) iris['species'] = tmp.target_names[tmp.target] iris.species = iris.species.astype('category') import seaborn as sns import matplotlib.pyplot as plt sns.set_style('darkgrid') sns.boxplot(x='species', y='sepal length (cm)', data=iris_new) plt.show() 。但是在接口构建器中链接的局部变量(隐式包装)也不会在UIViewController.init(nib...)处设置/实现。正确的方法需要两个调整:

  1. init(coder) var的设置移至program初始值设定项:
  2. e.g。

    init(coder)
    1. 将其转发到required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.program = self.site.newProgram() } 覆盖中的视图。
    2. e.g。

      viewDidLoad()