UIView 子类可以创建其他自定义视图实例吗?

Can a UIView subclass create other custom view instances?

我有一个名为 LifeCounter 的 UIView 子class,我想为每个玩家创建一个。我能够向 Main.storyboard 添加两个视图,通过“属性”面板将它们的 class 设置为 LifeCounter 并以此方式创建多个实例,连接到视图控制器,更改属性等

我现在想的是创建一个更大的视图 GameHeader,它将保存 LifeCounters 和一些其他补充信息,例如时间、游戏重置按钮等。GameHeader 是一个 UIView 子class,但我无法在模拟器中绘制我的 LifeCounter 视图,我不知道为什么。

GameHeader 当前是一个拖入情节提要中的视图,并且它在属性面板中 class。

GameHeader.swift

import UIKit

class GameHeader: UIView {

// MARK: Properties

// The Frame
let topFrame = CGRect(x: 0, y: 0, width: 320, height: 200)

// MARK: Initlization
required init() {
    super.init(frame: topFrame)

    // Adds the player one counter
    let playerOneCounter = LifeCounter()
    addSubview(playerOneCounter)

}

required init(coder aDecoder: NSCoder) {
    // Calls the super class (UIView) initializer
    super.init(coder: aDecoder)
}

}

LifeCounter.swift

import UIKit

class LifeCounter: UIView {

// MARK: Propterties

// Starting life total
var lifeTotal = 20 {
    didSet {
        // Updates the layout whenever the lifeTotal is updated
        setNeedsLayout()
    }
}

// Creates the UI Labels
// All created views need a defined frame for where they sit
// Is this neccesary outside of the init? Is there a better way?
var counter = UILabel(frame: CGRect(x: 0, y: 20, width: 100, height: 90))
var playerName = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
var winner = ""



// MARK: Initlization

// First init. LifeCounter takes a frame parameter, the adds the labels etc.
init() {
    super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
    self.addLifeCounter()
}

required init(coder aDecoder: NSCoder) {
    // Calls the super class (UIView) initializer
    super.init(coder: aDecoder)
}

func addLifeCounter() {
    print("addLifeCounter is running")
    // Styles life counter label
    counter.textColor = UIColor.blackColor()
    counter.textAlignment = .Center
    counter.font = UIFont.boldSystemFontOfSize(72)
    counter.text = String(lifeTotal)

    // Styles playerName label
    playerName.text = "Player Name"
    playerName.textAlignment = .Center


    // Button
    let minusButton = UIButton(frame: CGRect(x: 5, y: 110, width: 40, height: 40))
    let plusButton = UIButton(frame: CGRect(x: 55, y: 110, width: 40, height: 40))

    minusButton.backgroundColor = UIColor.redColor()
    plusButton.backgroundColor = UIColor.blueColor()

    // Button action
    minusButton.addTarget(self, action: "minusLife:", forControlEvents: .TouchDown)
    plusButton.addTarget(self, action: "plusLife:", forControlEvents: .TouchDown)

    addSubview(playerName)
    addSubview(counter)
    addSubview(minusButton)
    addSubview(plusButton)
}


// MARK: Button actions
func minusLife(minusButton: UIButton) {
    lifeTotal -= 1
    counter.text = String(lifeTotal)
}

func plusLife(plusButton: UIButton) {
    lifeTotal += 1
    counter.text = String(lifeTotal)
}

}

init(coder:) 是从故事板或笔尖创建视图时调用的初始化程序。如果您将创建和添加 LifeCounter 实例的代码移到那里,它应该可以工作。

使它更可重用的一个好策略是创建一个从两个初始化程序调用的设置方法,这样它将 运行 无论它来自 storyboard/nib 还是以编程方式实例化。

class GameHeader: UIView {
    let topFrame = CGRect(x: 0, y: 0, width: 320, height: 200)

    required init() {
        super.init(frame: topFrame)
        self.setupSubviews()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupSubviews()
    }

    func setupSubviews() {
        let playerOneCounter = LifeCounter()
        addSubview(playerOneCounter)
    }

}

我发现错误是将 LifeController 绘图代码放在 init(frame:) 块而不是 Coder 块中。通过 Interface Builder 添加视图时,它使用 init(coder:) 而不是 init(frame:)