swift - 从 subview 更改@IBOutlet

标签 swift iboutlet

我正在尝试从 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
    ...
}

我知道这很简单,但我不能让它工作。
情况如下: enter image description here

最佳答案

问题的根源是这样一行:

let editViewController = EditImageViewController()

EditImageViewController() 表示“忽略 Storyboard已经为我实例化的内容,而是实例化另一个没有连接导出的 View Controller 并使用它。”显然,这不是你想要的。

您需要为 EraseView 提供一些方法来通知现有的 View Controller 其“为空”状态是否发生了一些变化。而且,理想情况下,您希望以保持这两个类松散耦合的方式来执行此操作。 EraseView 应该只通知 View Controller “为空”状态的变化,并且 View Controller 应该启动其他 subview (即按钮)的更新。一个 View 真的不应该更新另一个 View 的导出。

有两种方法可以做到这一点:

  1. 关闭:

    您可以为 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 来避免强引用循环。

  2. 委托(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) 将自己指定为 EraseViewdelegate; (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/

相关文章:

ios - 从共享扩展中检索 imageURL

ios - 是否可以在代码中设置 Core Data 自定义类变量?

ios - 更改 spritekit 中的初始场景

ios - 无法设置引用 socket

ios - 无法将连接器从 IBOutlet 链接到 View Controller

ios - UITextDocumentProxy adjustTextPositionByCharacterOffset 问题

swift - 为什么我的 AVCapturePhotoOutput 文件输出这么大?

objective-c - 在@interface 内部或外部声明 IBOutlet?

swift - 以编程方式访问未安装的约束?

objective-c - NSWIndowController 创建 Nib 加载的 Outlets 序列