EXC_BAD_ACCESS,当从另一个调用按钮方法时 Class

EXC_BAD_ACCESS, When Calling Button Method From Another Class

我在 ViewControllerclass 中调用 DrawMenuclass 的方法,它绘制了一个椭圆形(当前为圆形)按钮,非常简单。它完美地绘制了按钮,但如果我点击按钮,它就会崩溃。

即使我在 DrawMenu 中创建了 ViewControllerclass 的实例,并将其用于 'button.addTarget' 中的 'target' 参数,也会发生这种情况]

代码如下:

DrawMenuclass中定义的按钮方法:

func drawButton (superImageView: UIImageView, x_of_origin: CGFloat, y_of_origin: CGFloat, width_of_oval: CGFloat, height_of_oval: CGFloat, actionSelector: Selector, want_to_test_bounds:Bool) {

    var VC = ViewController()

    var button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    button.addTarget(VC, action: actionSelector, forControlEvents: UIControlEvents.TouchUpInside) 
    button.frame = CGRect(x: x_of_origin, y: y_of_origin, width: width_of_oval, height: height_of_oval)

    button.clipsToBounds = true
    button.layer.cornerRadius = height_of_oval/2.0 

    if (want_to_test_bounds == true) {
        button.layer.borderColor = UIColor.blackColor().CGColor
        button.layer.borderWidth = 1.0
        superImageView.userInteractionEnabled = true
        superImageView.addSubview(button)

    } else {
        superImageView.userInteractionEnabled = true
        superImageView.addSubview(button)
    }

}

ViewControllerclass中调用的方法:

override func viewDidLoad() {
    super.viewDidLoad()

    var drawMenu = DrawMenu()

    drawMenu.drawButton(imageView, x_of_origin: 100, y_of_origin: 150, width_of_oval: 100, height_of_oval: 100, actionSelector: "buttonTap:" as Selector, want_to_test_bounds: true) 
}

buttonTap 也在 ViewController class:

    func buttonTap(sender:UIButton!){
    println("Button is working")
}

感谢任何帮助。 谢谢

在方法 drawButton 中,您将 touchUpInside 的目标设置为视图控制器的新实例。此引用在 drawButton 中的局部变量中创建,并将在该方法退出时释放。当动作处理程序被触发时,它会尝试调用无效对象上的函数,然后您会崩溃。

此处使用的正确设计模式是委托 - 为处理程序定义一个协议并让您的视图控制器实现该协议。然后,您可以将视图控制器传递给 drawButton 方法 -

首先在 DrawMenu 中定义协议 -

protocol ButtonDelegate:NSObjectProtocol
{
    func buttonTap(sender: UIButton!) -> Void
}

然后你可以在你的drawButton方法中使用协议引用-

func drawButton (superImageView: UIImageView, x_of_origin: CGFloat, y_of_origin: CGFloat, width_of_oval: CGFloat, height_of_oval: CGFloat, delegate: ButtonDelegate, want_to_test_bounds:Bool) {

    var button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    button.addTarget(delegate, action: "buttonTap:", forControlEvents: UIControlEvents.TouchUpInside) 
    button.frame = CGRect(x: x_of_origin, y: y_of_origin, width: width_of_oval, height: height_of_oval)

    button.clipsToBounds = true
    button.layer.cornerRadius = height_of_oval/2.0 

    if (want_to_test_bounds == true) {
        button.layer.borderColor = UIColor.blackColor().CGColor
        button.layer.borderWidth = 1.0
        superImageView.userInteractionEnabled = true
        superImageView.addSubview(button)

    } else {
        superImageView.userInteractionEnabled = true
        superImageView.addSubview(button)
    }

}

最后,确保你的 ViewController 实现了协议 -

class ViewController : UIViewController, ButtonDelegate

并在创建按钮时将引用传递给 ViewController 实例 -

override func viewDidLoad() {
    super.viewDidLoad()

    var drawMenu = DrawMenu()

    drawMenu.drawButton(imageView, x_of_origin: 100, y_of_origin: 150, width_of_oval: 100, height_of_oval: 100, delegate: self, want_to_test_bounds: true) 
}

我建议的其他改进是使 drawButton 方法成为静态方法,class 方法,这样您就不需要实例化 DrawMenu 的实例来使用它并让方法只是 return 按钮,而不是向方法传递一个视图以将按钮添加到其中。按照您的方式,如果您想进行进一步的更改,很难获得对该按钮的引用。以这种方式更改功能也使得将按钮添加到不是 UIImageViews

的视图变得简单

最后,使用 CGRect 而不是传递不同的 x、y、宽度、高度

class func drawButton (frame: CGRect, delegate: ButtonDelegate, showOutline:Bool) -> UIButton {

    var button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    button.addTarget(delegate, action: "buttonTap:", forControlEvents: UIControlEvents.TouchUpInside) 
    button.frame = frame
    button.clipsToBounds = true
    button.layer.cornerRadius = frame.size.height/2.0 

    if (showOutline) {
        button.layer.borderColor = UIColor.blackColor().CGColor
        button.layer.borderWidth = 1.0
    }
    return button
}

然后你会说-

override func viewDidLoad() {
    super.viewDidLoad()

    var newButton = DrawMenu.drawButton(CGRect(x: 100, y: 150, width: 100, height: 100), 
      delegate: self, 
      showOutline: true) 
    imageView.userInteractionEnabled = true
    imageView.addSubview(newButton)
}

向按钮添加目标时,设置正确的目标对象很重要。在您的情况下,您必须将 View Controller 对象传递给 drawButton 方法,并且在将目标添加到按钮时必须使用该 ViewController 对象。

因为当调用按钮事件时,它会在那个确切的目标中找到选择器! 因此,在您的情况下,当您在方法内部实例化一个 ViewController 对象时,该对象将在方法调用结束时被释放。

func drawButton (superImageView: UIImageView, inViewController: UIViewController, x_of_origin: CGFloat, y_of_origin: CGFloat, width_of_oval: CGFloat, height_of_oval: CGFloat, actionSelector: Selector, want_to_test_bounds:Bool)
{
    var button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
    button.addTarget(inViewController, action: actionSelector, forControlEvents: UIControlEvents.TouchUpInside)
    button.frame = CGRect(x: x_of_origin, y: y_of_origin, width: width_of_oval, height: height_of_oval)

    button.clipsToBounds = true
    button.layer.cornerRadius = height_of_oval/2.0

    if (want_to_test_bounds == true)
    {
        button.layer.borderColor = UIColor.blackColor().CGColor
        button.layer.borderWidth = 1.0
        superImageView.userInteractionEnabled = true
        superImageView.addSubview(button)

    }
    else
    {
        superImageView.userInteractionEnabled = true
        superImageView.addSubview(button)
    }
}