EXC_BAD_ACCESS,当从另一个调用按钮方法时 Class
EXC_BAD_ACCESS, When Calling Button Method From Another Class
我在 ViewController
class 中调用 DrawMenu
class 的方法,它绘制了一个椭圆形(当前为圆形)按钮,非常简单。它完美地绘制了按钮,但如果我点击按钮,它就会崩溃。
即使我在 DrawMenu
中创建了 ViewController
class 的实例,并将其用于 'button.addTarget' 中的 'target' 参数,也会发生这种情况]
代码如下:
DrawMenu
class中定义的按钮方法:
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)
}
}
ViewController
class中调用的方法:
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)
}
}
我在 ViewController
class 中调用 DrawMenu
class 的方法,它绘制了一个椭圆形(当前为圆形)按钮,非常简单。它完美地绘制了按钮,但如果我点击按钮,它就会崩溃。
即使我在 DrawMenu
中创建了 ViewController
class 的实例,并将其用于 'button.addTarget' 中的 'target' 参数,也会发生这种情况]
代码如下:
DrawMenu
class中定义的按钮方法:
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)
}
}
ViewController
class中调用的方法:
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)
}
}