Jim Jim - 1 month ago 14
Swift Question

Action on a UIButton doesn't work

I'm trying to understand the MVVM pattern by learning from a tutorial (link) and I can't get an action I've added to a UIButton to work (I'm using Swift 3.0). I've tried debugging for hours, studied this thread (@selector in swift) and others, and I'm still stuck. When I click the button, nothing happens at all. I tried various syntax options with the selector and I think what you see below is correct. I think the protocol I'm using might be contributing to the problem but, given my limited experience with protocols, I could be completely wrong about that.

I'll put all the code here in case anybody wants to drop it into Xcode. The action in question here is in my

UIViewController
that I add to
showGreetingButton
:

class GreetingViewController : UIViewController {
let showGreetingButton = UIButton()
@IBOutlet weak var greetingLabel: UILabel!

var viewModel: GreetingViewModelProtocol! {
didSet {
self.viewModel.greetingDidChange = { [unowned self] viewModel in
self.greetingLabel.text = viewModel.greeting
}
}
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

override func viewDidLoad() {
super.viewDidLoad()
showGreetingButton.backgroundColor = UIColor.red
showGreetingButton.tintColor = UIColor.black
showGreetingButton.setTitle("Show Greeting", for: .normal)

// I add the action here:
showGreetingButton.addTarget(self.viewModel, action: #selector(GreetingViewModel.showGreeting), for: .touchUpInside)

showGreetingButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(showGreetingButton)
let buttonTop = NSLayoutConstraint(item: showGreetingButton, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.greaterThanOrEqual, toItem: view, attribute: NSLayoutAttribute.topMargin, multiplier: 1.0, constant: 20)
let buttonLeading = NSLayoutConstraint(item: showGreetingButton, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.leadingMargin, multiplier: 1.0, constant: 0.0)
showGreetingButton.contentEdgeInsets = UIEdgeInsetsMake(2, 2, 2, 2)
NSLayoutConstraint.activate([buttonTop, buttonLeading])

let model = Person(firstName: "David", lastName: "Blaine")

let viewModel = GreetingViewModel(person: model)

self.viewModel = viewModel
}
}


My view model is called GreetingViewModel which adopts a protocol:

protocol GreetingViewModelProtocol: class {
var greeting: String? { get }
var greetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set }
init(person: Person)
func showGreeting()
}

class GreetingViewModel : GreetingViewModelProtocol {
let person: Person
var greeting: String? {
didSet {
self.greetingDidChange?(self)
}
}

var greetingDidChange: ((GreetingViewModelProtocol) -> ())?

required init(person: Person) {
self.person = person
}

@objc func showGreeting() {
self.greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
}
}


And my simple model is:

struct Person {
let firstName: String
let lastName: String
}


Any help would be really, really appreciated.

Answer

Please change the code like below. It worked for me

override func viewDidLoad() {
    super.viewDidLoad()
    showGreetingButton.backgroundColor = UIColor.red
    showGreetingButton.tintColor = UIColor.black
    showGreetingButton.setTitle("Show Greeting", for: .normal)
    let model = Person(firstName: "David", lastName: "Blaine")

    let viewModel = GreetingViewModel(person: model)

    self.viewModel = viewModel

    // I add the action here:
    showGreetingButton.addTarget(self.viewModel, action: #selector(GreetingViewModel.showGreeting), for: .touchUpInside)

    showGreetingButton.translatesAutoresizingMaskIntoConstraints = false    
    view.addSubview(showGreetingButton)
    let buttonTop = NSLayoutConstraint(item: showGreetingButton, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.greaterThanOrEqual, toItem: view, attribute: NSLayoutAttribute.topMargin, multiplier: 1.0, constant: 20)
    let buttonLeading = NSLayoutConstraint(item: showGreetingButton, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.leadingMargin, multiplier: 1.0, constant: 0.0)
    showGreetingButton.contentEdgeInsets = UIEdgeInsetsMake(2, 2, 2, 2)
    NSLayoutConstraint.activate([buttonTop, buttonLeading])

  }

Right now you are adding self.viewModel as the target, but self.viewModel is nil

Comments