I'm going through and learning Swift by porting an existing application. I'm stuck on setting a delegate and cannot work out what the issue is.
I have a class which extends UITableViewCell
import UIKit
protocol SwitchCellDelegate{
func switchChanged(switchCell: SwitchCell, state: Bool)
}
class SwitchCell: UITableViewCell {
@IBOutlet var swtSelector: UISwitch
@IBOutlet var lblTitle: UILabel
var delegate: SwitchCellDelegate?
init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
@IBAction func switchChanged(){
delegate?.switchChanged(self, state: swtSelector.on)
}
}
Then in the ViewController is defined as
class SettingsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SwitchCellDelegate {
and within the method
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
we have
case 2:
storeCredentialsCell = tableView.dequeueReusableCellWithIdentifier("StoreCredentialsCell") as? SwitchCell
if(storeCredentialsCell != nil){
...
NSLog("Setting delegate to %@ for %@", self.description, storeCredentialsCell.description)
storeCredentialsCell!.delegate = self
...
}
the log output is as expected but when it hits the actual setting of the delegate the app crashes with
EXC_BAD_ACCESS(code=1, address=0xfffffffffffffff8)
I should also note that if I don't set the delegate value when delegate?.switchChanged(self, state: swtSelector.on) fires it also causes an EXC_BAD_ACCESS error but according to the doco for delegates this should fail gracefully if delegate is not set to anything.
===========================
I've simplified down to a basic project to replicate the issue.
TestTableViewController.swift
import UIKit
class TestTableViewController: UITableViewController, TestCellDelegate {
init(style: UITableViewStyle) {
super.init(style: style)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? TestCell
if(cell != nil){
cell!.delegate = self
cell!.lblTest.text = "Test Successful"
}
return cell
}
func eventFired(sender: TestCell) {
NSLog("Hooray!")
}
TestCell.swift
import UIKit
protocol TestCellDelegate{
func eventFired(sender: TestCell)
}
class TestCell: UITableViewCell {
@IBOutlet var lblTest: UILabel
@IBOutlet var swtTest: UISwitch
var delegate: TestCellDelegate?
init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
@IBAction func switchChanged(sender: UISwitch){
delegate?.eventFired(self)
}
}
I then created a single Table View Controller Scene with Class of TestTableViewController. The table view is dynamic with a single cell of type TestCell. That cell contains one label and one switch which are bound to the IBOutlets from the TestCell class. The switchChanged function is bound to the value changed event on the switch.
Same EXC_BAD_ACCESS error is thrown.
Currently you have to explicitly mark your protocols with
@objc
if the delegate should be an Object of a Objective-C class (Like the UITableViewController):This will enable interoperating with Objective-C
And now for the solution....
instead of