简体   繁体   中英

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.

Move these lines to before you set your button's target:

let viewModel = GreetingViewModel(person: model)
self.viewModel = viewModel

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM