我正在尝试从 UIView 启用或禁用工具栏的 @IBOutlet UIButton 项。
当我在 EraseView.Swift 中使用的数组为空时,按钮应该被禁用。
我尝试创建 View Controller 的实例,但它给了我错误(展开时发现 nil):
在删除 View 中:
class EraseView: UIView {
...
let editViewController = EditImageViewController()
//array has item
editViewController.undoEraseButton.enabled = true //here I get the error
...
}
我试图在 EditImageViewController 中放置一个全局 Bool 来更改值,但它不起作用:
var enableUndoButton = false
class EditImageViewController: UIViewController {
@IBOutlet weak var undoEraseButton: UIBarButtonItem!
viewDidLoad() {
undoEraseButton.enabled = enableUndoButton
}
}
class EraseView: UIView {
...
//array has item
enableUndoButton = true //here I get the error
...
}
最佳答案
问题的根源是这样一行:
let editViewController = EditImageViewController()
EditImageViewController()
表示“忽略 Storyboard已经为我实例化的内容,而是实例化另一个没有连接导出的 View Controller 并使用它。”显然,这不是你想要的。
您需要为 EraseView
提供一些方法来通知现有的 View Controller 其“为空”状态是否发生了一些变化。而且,理想情况下,您希望以保持这两个类松散耦合的方式来执行此操作。 EraseView
应该只通知 View Controller “为空”状态的变化,并且 View Controller 应该启动其他 subview (即按钮)的更新。一个 View 真的不应该更新另一个 View 的导出。
有两种方法可以做到这一点:
关闭:
您可以为
EraseView
提供一个可选的闭包,当它从“空”和“非空”切换时将调用该闭包:var emptyStateChanged: ((Bool) -> ())?
然后它可以在状态改变时调用它。例如,当您删除 View 中的最后一项时,
EraseView
可以调用该闭包:emptyStateChanged?(true)
最后,为了真正做任何事情, View Controller 应该提供实际的闭包以在状态更改时启用和禁用按钮:
override func viewDidLoad() { super.viewDidLoad() eraseView.emptyStateChanged = { [unowned self] isEmpty in self.undoEraseButton.enabled = !isEmpty } }
请注意,我使用
unowned
来避免强引用循环。委托(delegate)协议(protocol)模式:
所以你可以定义一个协议(protocol)来做到这一点:
protocol EraseViewDelegate : class { func eraseViewIsEmpty(empty: Bool) }
然后给
EraseView
一个delegate
属性:weak var delegate: EraseViewDelegate?
请注意,
weak
是为了避免强引用循环。 (这也是我将协议(protocol)定义为class
协议(protocol)的原因,这样我就可以在此处将其设为weak
。)当 View 的“为空”状态发生变化时,
EraseView
将调用此委托(delegate)。例如,当它变空时,它会相应地通知它的委托(delegate):delegate?.eraseViewIsEmpty(true)
然后,为了让这一切正常工作, View Controller 应该(a)声明它符合协议(protocol); (b) 将自己指定为
EraseView
的delegate
; (c) 实现eraseViewIsEmpty
方法,例如:class EditImageViewController: UIViewController, EraseViewDelegate { @IBOutlet weak var undoEraseButton: UIBarButtonItem! override func viewDidLoad() { super.viewDidLoad() eraseView.delegate = self } func eraseViewIsEmpty(empty: Bool) { undoEraseButton.enabled = !empty } }
这两种模式都使两个类保持松散耦合,但允许 EraseView
将某些事件通知其 View Controller 。它还消除了对任何全局的需要。
还有其他方法也可以解决这个问题(例如通知、KVN 等),但希望这能说明基本思想。 View 应将任何关键事件通知其 View Controller ,而 View Controller 应负责其他 View 的更新。
关于swift - 从 subview 更改@IBOutlet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37077615/