首页 文章

Swift 2.2 - 更新UILabel抛出“在解开可选值时意外发现nil”

提问于
浏览
2

我是iOS开发的新手,请原谅我,如果我错过了一些明显的东西 . 我有一个视图控制器,其中包含一个子视图,我在其中创建了一个小键盘,目前我想给小键盘视图提供自己的UIView子类,因为我想用它做一些不同的事情 . 现在,numpad只是从按下的键创建一个字符串,我已经设置了一个委托来将该字符串传递给我想要使用的其他任何地方(虽然我也试过直接在视图中访问原始输入controller with a a = subview(); label.text = a.rawInput) .

每当我尝试将视图控制器中的UILabel文本设置为子视图的原始输入时,无论是通过委托还是直接,都会发现UILabel为nil并在 Headers 中引发错误 .

我试过的事情:

  • 在viewDidLoad覆盖内设置文本,并在其外部

  • 在视图控制器内设置变量(testInput)以采用子视图's raw input and setting the label text to that (I' ve确认视图控制器内的变量已正确设置,因此没有委托问题)

  • 在testInput变量上使用didSet将标签文本设置为testInput并尝试调用viewDidLoad并在其中设置标签文本(在此didSet中打印testInput会打印正确的字符串,FWIW)

  • 删除并重新链接我的标签的IBOutlet

  • IBOutlet变量的强存储和弱存储

  • 试图在视图控制器中的另一个子视图中执行相同的操作,如果由于某种原因它是视图控制器自己的错误

  • 在任何地方搜索有效的解决方案

我很难过 . 这是我的相关小键盘代码:

import UIKit

protocol NumpadDelegate {
func updateInput(input: String)
}

class Numpad: UIView {

// MARK: UI outlets
@IBOutlet weak var decButton: UIButton!


// MARK: Properties
var rawInput: String = ""
var visibleInput: String = ""
var calcInput: String = ""
var operandReady = 1
var percentWatcher = 0

var delegate: NumpadDelegate? = BudgetViewController()



// MARK: Functions
func handleRawInput(str: String) {
    rawInput += str
    print("numpad input is \(rawInput)")

    delegate?.updateInput(rawInput)
}

这是视图控制器代码:

import UIKit

class BudgetViewController: UIViewController, NumpadDelegate {

// MARK: Properties
//@IBOutlet weak var transactionValueField: UITextField!
@IBOutlet weak var remainingCashForIntervalLabel: UILabel!
@IBOutlet weak var intervalDenoterLabel: UILabel!
@IBOutlet weak var currencyDenoterLabel: UILabel!
@IBOutlet weak var mainDisplayView: TransactionType!
@IBOutlet weak var inactiveInputView: InactiveInput!
@IBOutlet weak var numpadView: Numpad!
@IBOutlet weak var rawInputLabel: UILabel!


var remainingCashForInterval = 40

let display = TransactionType()
var testInput = "" {
    didSet {
        viewDidLoad()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
//        let numpad = Numpad()
//        numpad.delegate = self
//        print("\(numpad.delegate)")
    self.rawInputLabel.text = testInput


}

func updateInput(input: String) {
    print("view controller now has \(input)")
    display.mainInput = input
    testInput = input
}

作为旁注,如果您注意到我的协议不是类类型,由于某种原因添加:class并将我的委托声明为弱变量会阻止委托工作 . 有什么建议吗?

1 回答

  • 2

    你像这样分配了代表:

    var delegate: NumpadDelegate? = BudgetViewController()
    

    当你使用 weak 时,为什么它被解除分配(因为视图控制器的孤立实例没有对它的强引用),这并不是什么原因 .

    您应该再次将协议定义为 class 协议,并将 delegate 定义为:

    weak var delegate: NumpadDelegate?
    

    然后,在视图控制器的 viewDidLoad 中,取消注释设置该委托的行:

    numpadView.delegate = self
    

    但是,不要取消注释 numpad = Numpad() ;这是不正确的,因为它创建了另一个 Numpad 实例 . 但是,您确实想要设置现有 Numpad 的委托 .


    这两个问题(即,获取对作为 Numpad 视图的委托的视图控制器的引用;以及获取对故事板呈现的 Numpad 视图的引用)表明对呈现故事板场景的过程存在一些误解 .

    该过程基本如下:

    • 视图控制器是实例化的,使用您指定的任何类作为该场景的基类;

    • 其根 view ,以及该场景上的所有子视图都将为您实例化;

    • 故事板会将场景基类中的 IBOutlet 引用连接到您创建的出口;和

    • 调用视图控制器的 viewDidLoad .

    这是过于简单化,但这是基本过程 .

    但关键是故事板场景中引用的所有这些视图控制器和视图都是为您创建的 . 你不想尝试自己创建任何这些(并且 BudgetViewController()Numpad() 末尾的 () 表示“创建一个新的x实例”,这不是我们想要做的) .

    因此,当我们需要获取对视图控制器的引用以便我们可以以编程方式为其中一个视图指定 delegate 时,可以在 viewDidLoad 中执行此操作,此时 self 引用故事板为我们实例化的视图控制器 . 我们不想实例化一个新的 . 同样,当你想引用故事板为我们实例化的 Numpad 时(为了连接它的 delegate ),你使用你在Interface Builder中连接的 IBOutlet ,而不是以编程方式用 Numpad() 实例化一个新的 Numpad .

相关问题