繁体   English   中英

如何向另一个类中的UIButton添加操作

[英]How to add an action to a UIButton that is in another class

下面的代码可以正常编译,但是由于unrecognized selector sent to instance错误而崩溃。

我有一个从UIViewController继承的类:

class Controller: UIViewController {
    override func viewDidLoad() {
        let toolbarWrapper = CustomToolbarWrapper(view: view, target: self)
        let toolbar = toolbarWrapper.toolbarView
        view.addSubview(toolbar)

        ... Other code ...

    }
}

另一个类只是UIView的包装器,它包含按钮:

class CustomToolbarWrapper {

    var toolbarView: UIView

    init(view: UIView, target: Any) {
        let height: CGFloat = 80
        toolbarView = UIView(frame: CGRect(x: 0, y: view.frame.height - height, width: view.frame.width, height: height))
        let button = UIButton()

        ... Some button layout code ...

        button.addTarget(target, action: #selector(CustomToolbar.buttonTapped(_:)), for: .touchUpInside)
        toolbarView.addSubview(button)
    }

    @objc static func buttonTapped(_ sender: Any) {
        print("button tapped")
    }
}

为了清楚起见,我省略了大量代码,并保留了我认为必要的内容。 我认为我的代码无法正常工作是因为我对addTarget函数中目标的工作方式有误解。 通常,我只是将self用作按钮操作的目标,因此我只是尝试将self从视图控制器传递到CustomToolbarWrapperinit函数。

我还尝试了什么:

像这样将按钮的目标从target更改为self

button.addTarget(self, action: #selector(CustomToolbar.buttonTapped(_:)), for: .touchUpInside)

导致应用不再崩溃。 但是,我认为代码行无法执行任何操作(由于某种原因不会引发错误?),因为尝试打印button.allTargets甚至button.allTargets.count导致应用在编译时崩溃,带有EXC_BREAKPOINT错误,并且控制台或XCode UI中没有错误描述(这使我更加困惑,因为我的代码中没有断点!)。

同样,将buttonPressed(_:)非静态不会更改任何前面提到的观察结果。

另外,为确保按钮实际上可以与之交互,我在ControllerviewDidLoad()中添加了此按钮:

for subview in toolbar.subviews? {
    if let button = subview as? UIButton {
        button.addTarget(self, action: #selector(buttonPressed(_:)), for: .touchUpInside)
    }
}

并为Controller添加了一种简单的测试方法:

@objc func buttonPressed(_ sender: UIButton) {
    print("Button Pressed")
}

运行代码确实会导致在控制台日志中打印“已按下按钮”,因此用户应该可以与该按钮进行交互。

如果您认为此代码不足以解决问题,请随时告诉我,我将发布更多详细信息。

编辑我更喜欢将按钮动作的实现保留在CustomToolbarWrapper类中,以防止将来重复代码,因为无论在何处创建CustomToolbarWrapper实例,该动作都是相同的。

您的问题就在这里:

let toolbarWrapper = CustomToolbarWrapper(view: view, target: self)

您正在传递Controller类的实例,该实例未实现buttonTapped(_:)选择器。 它由CustomToolbarWrapper类实现。 通常这是一个不好的设计。 您应该遵循委托模式或回调模式。

更新的答案:

委托模式解决方案:

class Controller: UIViewController, CustomToolbarWrapperDelegate {
    override func viewDidLoad() {
        let toolbarWrapper = CustomToolbarWrapper(view: view, buttonDelegate: self)
        let toolbar = toolbarWrapper.toolbarView
        view.addSubview(toolbar)
    }

    // MARK: - CustomToolbarWrapperDelegate
    func buttonTapped(inToolbar toolbar: CustomToolbarWrapper) {
        print("button tapped")
    }
}

protocol CustomToolbarWrapperDelegate: AnyObject {
    func buttonTapped(inToolbar toolbar: CustomToolbarWrapper) -> Void
}

class CustomToolbarWrapper {

    var toolbarView: UIView
    weak var buttonDelegate: CustomToolbarWrapperDelegate?

    init(view: UIView, buttonDelegate: CustomToolbarWrapperDelegate?) {
        let height: CGFloat = 80
        toolbarView = UIView(frame: CGRect(x: 0, y: view.frame.height - height, width: view.frame.width, height: height))
        self.buttonDelegate = buttonDelegate
        let button = UIButton()
        button.addTarget(self, action: #selector(self.buttonTapped(_:)), for: .touchUpInside)
        toolbarView.addSubview(button)
    }

    @objc private func buttonTapped(_ sender: Any) {
        // Your button's logic here. Then call the delegate:
        self.buttonDelegate?.buttonTapped(inToolbar: self)
    }

}

如果您希望坚持当前的设计,则只需进行以下更改:

class Controller: UIViewController {
    override func viewDidLoad() {
        let toolbarWrapper = CustomToolbarWrapper(view: view, target: self, selector: #selector(self.buttonTapped(_:)), events: .touchUpInside)
        let toolbar = toolbarWrapper.toolbarView
        view.addSubview(toolbar)
    }

    @objc private func buttonTapped(_ sender: Any) {
        print("button tapped")
    }
}

class CustomToolbarWrapper {

    var toolbarView: UIView

    init(view: UIView, target: Any?, selector: Selector, events: UIControlEvents) {
        let height: CGFloat = 80
        toolbarView = UIView(frame: CGRect(x: 0, y: view.frame.height - height, width: view.frame.width, height: height))
        let button = UIButton()

        button.addTarget(target, action: selector, for: events)
        toolbarView.addSubview(button)
    }

}

最好的选择是将目标添加到您的控制器中,然后在按下按钮时在您的toolbarWrapper调用一个方法。 但是,如果您确实需要保留此设计,则应该在控制器类中强烈引用您的toolbarWrapper ,否则您的toolbarWrapper被释放,并且不会被调用。 同样, buttonTapped(_:)方法也不必是静态的。 因此,在您的控制器中:

class Controller: UIViewController {

    var toolbarWrapper: CustomToolbarWrapper?

    override func viewDidLoad() {
        toolbarWrapper = CustomToolbarWrapper(view: view, target: self)
        let toolbar = toolbarWrapper.toolbarView
        view.addSubview(toolbar)

        ... Other code ...

    }
}

在你的包装中:

class CustomToolbarWrapper {

    var toolbarView: UIView

    init(view: UIView, target: Any) {
        let height: CGFloat = 80
        toolbarView = UIView(frame: CGRect(x: 0, y: view.frame.height - height,width: view.frame.width, height: height))
        let button = UIButton()

        ... Some button layout code ...

        button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
        toolbarView.addSubview(button)
    }

    @objc func buttonTapped(_ sender: Any) {
        print("button tapped")
    }
}

我要使用的另一种方法是委派。 target不一定必须是控制器,它可以是CustomToolbarWrapper本身。 首先,声明一个协议

protocol CTDelegate: AnyObject {
  func didClickButton()
}

然后在CustomToolbarWrapper添加一个属性, weak var delegate: CTDelegate? 和一个按钮动作:

@objc func buttonTapped(_ sender: UIButton) {

   delegate?.didClickButton()
}

因此,在您的情况下,它变为:

button.addTarget(self, action: #selector(CustomToolbarWrapper.buttonTapped(_:)), for: .touchUpInside)

然后,当您转到任何ViewController时,请遵循CTDelegate并初始化CustomToolbarWrapper ,您可以将其委托设置为控制器。 例如

let toolbarWrapper = CustomToolbarWrapper(view: view, target: self)
toolbarWrapper.delegate = self

并在您要遵循的方法中执行您的操作,例如

func didClickButton()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM