Search code examples
swiftuibuttonxcode7.3swift2

Handler Method in #selector Swift 2.2


My idea here was to create a easy way to add an event method to a button.

Follow my example: Gist

The problem is that i'm having problem with EXC_BAD_ACCESS and probably is because the execution in the #selector is not safe.

Would be a way to fix or change to resolve it?

Debug Screen: DebugScreen

Stacktrace:

* thread #1: tid = 0x4e8e43, 0x0000000110355547 libsystem_blocks.dylib`_Block_copy_internal + 239, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x0000000110355547 libsystem_blocks.dylib`_Block_copy_internal + 239
frame #1: 0x000000010d8c326d Swift-201-Aula2-Exercicio2.1`@objc UICallbackButton.executeCallback(UIButton, callback : (button : UIButton) -> ()) -> () + 45 at ViewController.swift:0
frame #2: 0x000000010e2d4a8d UIKit`-[UIApplication sendAction:to:from:forEvent:] + 92
frame #3: 0x000000010e447e67 UIKit`-[UIControl sendAction:to:forEvent:] + 67
frame #4: 0x000000010e448143 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 327
frame #5: 0x000000010e447263 UIKit`-[UIControl touchesEnded:withEvent:] + 601
frame #6: 0x000000010e34799f UIKit`-[UIWindow _sendTouchesForEvent:] + 835
frame #7: 0x000000010e3486d4 UIKit`-[UIWindow sendEvent:] + 865
frame #8: 0x000000010e2f3dc6 UIKit`-[UIApplication sendEvent:] + 263
frame #9: 0x000000010e2cd553 UIKit`_UIApplicationHandleEventQueue + 6660
frame #10: 0x000000010d9d0301 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #11: 0x000000010d9c622c CoreFoundation`__CFRunLoopDoSources0 + 556
frame #12: 0x000000010d9c56e3 CoreFoundation`__CFRunLoopRun + 867
frame #13: 0x000000010d9c50f8 CoreFoundation`CFRunLoopRunSpecific + 488
frame #14: 0x0000000112140ad2 GraphicsServices`GSEventRunModal + 161
frame #15: 0x000000010e2d2f09 UIKit`UIApplicationMain + 171
* frame #16: 0x000000010d8c42c2 Swift-201-Aula2-Exercicio2.1`main + 114 at AppDelegate.swift:12
frame #17: 0x000000011031292d libdyld.dylib`start + 1

Solution

  • Your code has a couple problems, I believe. A circular reference on the button (might not be a problem), but for sure your callback isn't making it all the way through to the executeCallback(:) method. Turns out you don't need it to. Just save a reference to the callback. See if this makes sense. It works.

    import UIKit
    
    class ViewController: UIViewController {
    
        var count = 1
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Button 1
            let button = UICallbackButton(frame: CGRect(x: 60, y: 30, width: 200, height: 60))
            button.backgroundColor = UIColor.orangeColor()
            button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
            button.setTitle("Title Default", forState: .Normal)
            button.on(.TouchUpInside, then: {
                button.setTitle("Title \(self.count)", forState: .Normal)
                self.count = self.count + 1
            })
    
            self.view.addSubview(button)
    
    
            // Button 2
            let button1 = UICallbackButton(frame: CGRect(x: 60, y: 160, width: 200, height: 60))
            button1.backgroundColor = UIColor.blackColor()
            button1.setTitleColor(UIColor.whiteColor(), forState: .Normal)
            button1.setTitle("Value Default", forState: .Normal)
            button1.on(.TouchUpInside, then: {
                button1.setTitle("Value \(self.count)", forState: .Normal)
                self.count = self.count + 1
            })
    
            self.view.addSubview(button1)
    
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    }
    
    
    typealias ButtonCallback = (() -> ())
    
    protocol UIActionButton {
    
        func on(event : UIControlEvents, then callback: ButtonCallback)
    
    }
    
    final class UICallbackButton : UIButton, UIActionButton {
    
        var buttonCallback: ButtonCallback!
    
        func on(event : UIControlEvents, then callback: ButtonCallback) {
            buttonCallback = callback
            self.addTarget(self, action: #selector(UICallbackButton.executeCallback(_:)), forControlEvents: event)
    
        }
    
        func executeCallback (_ : UIButton) {
            buttonCallback()
        }
    }