WKCrownSequencer not working after going back to main interface controller - swift

I have a WKCrownSequencer that triggers an action in my pushed interface controller and the first time through everything works fine. When I go back to root interface controller regardless of the method (pop or reloadRootcontrollers) the digital crown no longer works in first interface controller nor the second one. The StartInterfaceController is the rootInterfaceController and the MidWorkoutInterfaceController is the pushed one.
import WatchKit
import Foundation
class StartInterfaceController:
WKInterfaceController,CLLocationManagerDelegate {
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
#IBAction func start() {
WKInterfaceController.reloadRootControllers(
withNames: ["midWorkout"], contexts: []
)
}
The second interface controller is below.
import WatchKit
import Foundation
class MidWorkoutInterfaceController: WKInterfaceController, WKCrownDelegate {
override func awake(withContext context: Any?) {
super.awake(withContext: context)
print("viewdidAwake")
print("ViewWillActivate")
crownSequencer.delegate = self
crownSequencer.focus()
WKInterfaceDevice.current().play(.success)
currentPhase = 0
let workoutType = UserDefaults.standard.object(forKey: "CurrentType") as? [String] ?? [ "Swimming", "T1"]
orderOfEventsSetter = workoutType
updateCurrentPhase()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
crownSequencer.focus()
}
var clockTimer: Timer!
func workoutStarted(){
clockTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
self?.totalTimeOutlet.setText( String(describing: -1 * Int(self!.startDate!.timeIntervalSinceNow)))
self?.splitTimeOutlet.setText( String(describing: -1 * Int(self!.currentStartDate!.timeIntervalSinceNow)))
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
var startDate: Date?
var currentStartDate: Date?
//Outlets
#IBOutlet var currentPhaseOutlet: WKInterfaceLabel!
#IBOutlet var totalTimeOutlet: WKInterfaceLabel!
#IBOutlet var splitTimeOutlet: WKInterfaceLabel!
#IBOutlet var currentPaceOutlet: WKInterfaceLabel!
#IBOutlet var totalDistanceOutlet: WKInterfaceLabel!
var orderOfEventsSetter: Array<String>{
get{
return orderOfEvents
}
set{
var tempArray = ["GPS Locking In"]
for phase in newValue {
tempArray.append(phase)
}
orderOfEvents = tempArray
}
}
private var orderOfEvents: [String] = []
var currentPhase = 0 {
didSet{
if !orderOfEvents.isEmpty {
updateCurrentPhase()
}
}
}
func updateCurrentPhase(){
currentPhaseOutlet.setText(orderOfEvents[currentPhase])
}
//timing for location requests
//Corelocation Section
//CoreMotion Section
///crown control
var currentDialRotation = 0.0
let dialRotationRange = Range(uncheckedBounds: (lower: -Double.pi / 4, upper: Double.pi / 4))
let constantForTimer: TimeInterval = 0.1
var justTransitioned = false
func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
currentDialRotation += rotationalDelta
if !dialRotationRange.contains(currentDialRotation){
currentDialRotation = 0.0
justTransitioned = true
makeTransition()
//make so two transitions cannot happen right after each other
}
print(currentDialRotation)
}
func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
print(String(describing: orderOfEvents[currentPhase]))
print("crown stopped")
}
func makeTransition(){
print(currentPhase)
print(orderOfEvents.count)
if (currentPhase) == orderOfEvents.count - 1 {
endWorkout()
}
else if (currentPhase == 0){
WKInterfaceDevice.current().play(.start)
let dateFormat = DateFormatter()
dateFormat.dateFormat = "mm/dd/yyyy"
startDate = Date()
currentStartDate = Date()
workoutStarted()
currentPhase += 1
}
else{
WKInterfaceDevice.current().play(.start)
print("transitioning to " + String(describing: orderOfEvents[currentPhase + 1]))
currentStartDate = Date()
stopTimers()
currentPhase += 1
}
}
#IBAction func endWorkoutButton() {
endWorkout()
}
func endWorkout(){
stopTimers()
clockTimer.invalidate()
alerts()
}
func alerts(){
let saveAction = WKAlertAction(title: "Save",
style: WKAlertActionStyle.default) {
self.goToStartScreen()
}
let discardAction = WKAlertAction(title: "Discard Workout",
style: WKAlertActionStyle.cancel) {
self.goToStartScreen()
}
presentAlert(withTitle: "Workout Complete",
message: "Would you like to save the workout?",
preferredStyle: WKAlertControllerStyle.alert,
actions: [saveAction, discardAction])
}
func goToStartScreen(){
crownSequencer.resignFocus()
self.popToRootController()
}
func stopTimers(){
if orderOfEvents[currentPhase] == "Running"{
}
if orderOfEvents[currentPhase] == "Biking"{
}
if currentPhase == orderOfEvents.count {
clockTimer.invalidate()
}
}
}

According to Apple's document here:
"...Only one object in your interface can have focus at any given time, so if your interface also contains picker objects or has scrollable scenes, you must coordinate changes in focus accordingly. For example, calling the sequencer's focus method causes any picker objects or interface controllers to resign focus. When the user taps on a picker object, the currently active sequencer resigns focus, and the selected picker object gains the focus... "
And you are to lose the focus at any time unpredictable...
"...If the user taps a picker object or a scrollable scene in your interface, the system automatically removes the focus from any active crown sequencer..."

Related

how to init() a swift view controller properly?

I'm attempting to initialize this ViewController class. I am not using the MVC design strategy so ignore the bad conventions used (if any).
How do I initialize this class properly?
Error: 'required' initializer 'init(coder:)' must be provided by subclass of 'UIViewController'
Context: This is a calculator app that when any of the buttons are pressed. It will go find the senders title and simply put if one of the three vars are nil, it will store it in that optional.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet weak var answerLabel: UILabel!
//global vars for all funcs
var selection1: Int? {
didSet { answerLabel.text = String(selection1!) }
}
var selection2: String? {
didSet { answerLabel.text = selection2! }
}
var selection3: Int? {
didSet { answerLabel.text = String(selection3!) }
}
var answer: Int {
didSet { answerLabel.text = String(answer) }
}
init() {
}
#IBAction func touchButton(_ sender: UIButton) {
if selection1 == nil {
selection1 = Int(sender.currentTitle!)
print("Selection set in first pos.")
} else if selection2 == nil {
selection2 = sender.currentTitle
} else if selection3 == nil {
selection3 = Int(sender.currentTitle!)
} else {
calculate(firstNum: selection1!, operation: selection2!, secondNum: selection3!)
}
}
func calculate(firstNum: Int, operation: String, secondNum: Int) {
switch operation {
case "+":
answer = firstNum + secondNum
case "-":
answer = firstNum - secondNum
case "x":
answer = firstNum * secondNum
case "/":
answer = firstNum / secondNum
default:
answerLabel.text = "Something went wrong!"
}
}
}
Initialization depends on a couple of condition.
If you are using storyboard, you can just remove the init and your VC will have default initializer. Make sure either all of your properties have default value or they are optional.
If you are using xib or just creating view programmatically you can have custom convenience initializer where you pass some extra data this way.
class MyViewController: ViewController {
var answer: Int
convenience init(answer: Int) {
self.init()
self.answer = answer
// Do other setup
}
}
Your controller is being instantiated from the storyboard. A safe place to configure initial views is during the controller's call to viewDidLoad, ie:
override func viewDidLoad() {
super.viewDidLoad()
// configure your views and subviews here
}

How can I get the game to show the word after all guesses have been used up?

so I'm new to coding and I've been doing practice games a such to build my skills. I've created this word guessing game and I'm trying to make the game show the word after all guesses have been used up. However, the program doesn't read the code I write to set the label to display the answer. Here is the code I've written so far:
class ViewController: UIViewController {
var listOfWords = ["ladybug", "program", "computer", "language", "glorious", "incandescent"]
let incorrectMovesAllowed = 7
var totalWins = 0 {
didSet {
newRound()
}
}
var totalLosses = 0 {
didSet {
newRound()
}
}
#IBOutlet var letterButtons: [UIButton]!
#IBAction func buttonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)!
let letter = Character(letterString.lowercased())
currentGame.playerGuessed(letter: letter)
updateUI()
updateGameState()
}
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var correctWordLabel: UILabel!
#IBOutlet weak var treeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
newRound()
// Do any additional setup after loading the view, typically from a nib.
}
func enableLetterButtons (_enable: Bool){
for button in letterButtons {
button.isEnabled = _enable
}
}
var currentGame : Game!
func newRound() {
if !listOfWords.isEmpty{
let newWord = listOfWords.removeFirst()
currentGame = Game (word: newWord, incorrectMovesRemaining: incorrectMovesAllowed, guessedLetters: [])
enableLetterButtons(_enable: true)
updateUI()
} else {
enableLetterButtons (_enable: false)
}
}
func updateUI() {
var letters = [String] ()
for letter in currentGame.formattedWord.characters {
letters.append(String(letter))
}
let wordWithSpacing = letters.joined(separator: " ")
correctWordLabel.text = wordWithSpacing
scoreLabel.text = "Wins: \(totalWins), Losses:\(totalLosses)"
treeImageView.image = UIImage (named: "Tree \(currentGame.incorrectMovesRemaining)")
}
func updateGameState(){
var letters = [String] ()
for letter in currentGame.word.characters {
letters.append(String(letter))
}
let theAnswer = letters.joined(separator: " ")
if currentGame.incorrectMovesRemaining == 0 {
correctWordLabel.text = theAnswer
Thread.sleep(forTimeInterval: 3)
totalLosses += 1
} else if currentGame.word == currentGame.formattedWord {
totalWins += 1
} else {
updateUI()
}
}
}
In addition, I have a structure that is written like this:
import Foundation
struct Game {
var word : String
var incorrectMovesRemaining : Int
var guessedLetters: [Character]
mutating func playerGuessed (letter: Character){
guessedLetters.append(letter)
if !word.characters.contains(letter){
incorrectMovesRemaining -= 1
}
}
var formattedWord: String {
var guessedWord = ""
for letter in word.characters {
if guessedLetters.contains(letter) {
guessedWord += "\(letter)"
} else {
guessedWord += "_"
}
}
return guessedWord
}
}
You need to redraw your UI, this is done with self.setNeedsDisplay(). It notifies the system that the view's contents needs to be redrawn. In your updateUI() you can add this.
Regarding setNeedsDisplay you can get more information here
class ViewController: UIViewController {
var listOfWords = ["ladybug", "program", "computer", "language", "glorious", "incandescent"]
let incorrectMovesAllowed = 7
var totalWins = 0 {
didSet {
newRound()
}
}
var totalLosses = 0 {
didSet {
newRound()
}
}
#IBOutlet var letterButtons: [UIButton]!
#IBAction func buttonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)!
let letter = Character(letterString.lowercased())
currentGame.playerGuessed(letter: letter)
updateUI()
updateGameState()
}
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var correctWordLabel: UILabel!
#IBOutlet weak var treeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
newRound()
// Do any additional setup after loading the view, typically from a nib.
}
func enableLetterButtons (_enable: Bool){
for button in letterButtons {
button.isEnabled = _enable
}
}
var currentGame : Game!
func newRound() {
if !listOfWords.isEmpty{
let newWord = listOfWords.removeFirst()
currentGame = Game (word: newWord, incorrectMovesRemaining: incorrectMovesAllowed, guessedLetters: [])
enableLetterButtons(_enable: true)
updateUI()
} else {
enableLetterButtons (_enable: false)
}
}
func updateUI() {
var letters = [String] ()
for letter in currentGame.formattedWord.characters {
letters.append(String(letter))
}
let wordWithSpacing = letters.joined(separator: " ")
correctWordLabel.text = wordWithSpacing
scoreLabel.text = "Wins: \(totalWins), Losses:\(totalLosses)"
treeImageView.image = UIImage (named: "Tree \(currentGame.incorrectMovesRemaining)")
self.setNeedsDisplay()
}
func updateGameState(){
var letters = [String] ()
for letter in currentGame.word.characters {
letters.append(String(letter))
}
let theAnswer = letters.joined(separator: " ")
if currentGame.incorrectMovesRemaining == 0 {
correctWordLabel.text = theAnswer
Thread.sleep(forTimeInterval: 3)
totalLosses += 1
} else if currentGame.word == currentGame.formattedWord {
totalWins += 1
} else {
updateUI()
}
}
}
Create a variable that will keep track of how many times you have guessed wrong. Then you can do this:
Use a while statement:
while numberOfTimesGuessedWrong <= 7{
}
//when you have guessed incorrectly 7 times, the compiler will move here:
wordLabel.text = "\(correctAnswer)"
So when you guess incorrectly 7 times, on the 8th time, it will then show the correct answer.

How to pass data backwards to a view controller after passing forward?

Im developing a quiz app and there is a second view controller that appears after the initial view controller where you are asked to answer a question. On the second view controller you the user must press a button to return to the initial view controller to be asked another question. However when I segue back from the second view controller I believe a new instance of the initial view controller is being created and the user is asked questions they have already answered. The code in the swift file for my initial view controller is constructed that when once a user is asked a question once that question is removed from an array by it's index. How could I make it to where a new instance of the initial view controller isn't created from segueing from the second view controller? Or is there a way the second view controller can access the same methods as the initial view controller?
Here is my code for the initial view controller:
import UIKit
class ViewController: UIViewController {
var questionList = [String]()
func updateCounter() {
counter -= 1
questionTimer.text = String(counter)
if counter == 0 {
timer.invalidate()
wrongSeg()
counter = 15
}
}
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
questionTimer.text = String(counter)
}
let rand = Int(arc4random_uniform(UInt32(questionList.count)))
questionLabel.text = questionList[rand]
//matching answer values to go with question keys
var choices = QADictionary[questionList[rand]]!
questionList.remove(at: rand)
//create button
var button:UIButton = UIButton()
//variables
var x = 1
rightAnswerBox = arc4random_uniform(4)+1
for index in 1...4
{
button = view.viewWithTag(index) as! UIButton
if (index == Int(rightAnswerBox))
{
button.setTitle(choices[0], for: .normal)
}
else {
button.setTitle(choices[x], for: .normal)
x += 1
}
randomImage()
}
}
let QADictionary = ["Who is Thor's brother?" : ["Atum", "Loki", "Red Norvell", "Kevin Masterson"], "What is the name of Thor's hammer?" : ["Mjolinr", "Uru", "Stormbreaker", "Thundara"], "Who is the father of Thor?" : ["Odin", "Sif", "Heimdall", "Balder"]]
//wrong view segue
func wrongSeg() {
performSegue(withIdentifier: "incorrectSeg", sender: self)
}
//proceed screen
func rightSeg() {
performSegue(withIdentifier: "correctSeg", sender: self)
}
//variables
var rightAnswerBox:UInt32 = 0
var index = 0
//Question Label
#IBOutlet weak var questionLabel: UILabel!
//Answer Button
#IBAction func buttonAction(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerBox))
{
rightSeg()
print ("Correct!")
}
if counter != 0 {
counter = 15
questionTimer.text = String(counter)
}
else if (sender.tag != Int(rightAnswerBox)) {
wrongSeg()
print ("Wrong!")
timer.invalidate()
questionList = []
}
randomQuestion()
}
override func viewDidAppear(_ animated: Bool)
{
randomQuestion()
}
//variables
var counter = 15
var timer = Timer()
#IBOutlet weak var questionTimer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
Here's the code to the second view controller:
import UIKit
class ContinueScreen: UIViewController {
//correct answer label
#IBOutlet weak var correctLbl: UILabel!
//background photo
#IBOutlet weak var backgroundImage: UIImageView!
func backToQuiz() {
if let nav = self.navigationController {
nav.popViewController(animated: true)
}
else {
self.dismiss(animated: true, completion: nil)
}
}
#IBAction func `continue`(_ sender: Any) {
backToQuiz()
}
What you need, sir is a delegate or an unwind segue. I far prefer delegates because they're easier to understand and I think a bit more powerful.
In your ContinueScreen view controller define a protocol similar to this:
protocol QuizCompletedDelegate {
func completedQuiz()
func canceledQuiz()
}
Also in your ContinueScreen view controller you need to declare an optional delegate of type QuizCompletedDelegate
var delegate: QuizCompletedDelegate?
... and use that delegate to call the functions when appropriate:
#IBAction func didPressContinue(_ sender: Any) {
if allQuestionsAnswered == true {
delegate.completedQuiz()
} else {
delegate.cancelledQuiz()
}
}
In your initial view controller is where you implement the functions for the protocol:
extension ViewController: QuizCompletedDelegate {
func completedQuiz() {
//Handle quiz complete
}
func cancelledQuiz() {
//Handle quiz cancelled
}
}
Then the last thing you need to do is set the delegate of your ContinueScreen view controller in the prepareForSegue function of your initial view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showContinueScreen" {
let continueVC = segue.destination as! ContinueScreen
continueVC.delegate = self
}
}
Remove the call to randomQuestion() on your initial view controller's ViewDidAppear, and you're in business!
It shouldn't create a new View Controller when you go back. If it does - it's very bad - and you should re-architect the flow of your app... To pass data forward, we should use Dependency Injection. To pass data to the previous view controller - use the iOS Delegation Pattern. I recommend to spend 30-40 mins to read (and understand this article), and it will make things more clear for you in the future: http://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/.

How to make a timer reset after button gesture in a different view controller?

I am developing a quiz app where the user is asked a question at random and must answer it on the initial view controller. If the user picks correctly, a second view controller appears which contains a button that pops the view controller off the navigation stack and goes back to the initial view controller to finish the other questions. However, I have a timer that I want to reset (start at 15s) every time that second view controller is popped and the initial view controller appears with the next question. How would I accomplish this task? I already have the countdown timer code in my swift file. I just need to know how to get it to start from scratch every time the second view controller is popped/removed.
Here's my code for the initial view controller:
import UIKit
extension ViewController: QuizCompletedDelegate {
func continueQuiz() {
randomQuestion()
}
}
class ViewController: UIViewController {
var questionList = [String]()
func updateCounter() {
counter -= 1
questionTimer.text = String(counter)
if counter == 0 {
timer.invalidate()
wrongSeg()
counter = 15
}
}
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
}
let rand = Int(arc4random_uniform(UInt32(questionList.count)))
questionLabel.text = questionList[rand]
//matching answer values to go with question keys
var choices = QADictionary[questionList[rand]]!
questionList.remove(at: rand)
//create button
var button:UIButton = UIButton()
//variables
var x = 1
rightAnswerBox = arc4random_uniform(4)+1
for index in 1...4 {
button = view.viewWithTag(index) as! UIButton
if (index == Int(rightAnswerBox)) {
button.setTitle(choices[0], for: .normal)
} else {
button.setTitle(choices[x], for: .normal)
x += 1
}
randomImage()
}
}
let QADictionary = ["Who is Thor's brother?" : ["Atum", "Loki", "Red Norvell", "Kevin Masterson"], "What is the name of Thor's hammer?" : ["Mjolinr", "Uru", "Stormbreaker", "Thundara"], "Who is the father of Thor?" : ["Odin", "Sif", "Heimdall", "Balder"]]
//wrong view segue
func wrongSeg() {
performSegue(withIdentifier: "incorrectSeg", sender: self)
}
//proceed screen
func rightSeg() {
performSegue(withIdentifier: "correctSeg", sender: self)
}
//variables
var rightAnswerBox:UInt32 = 0
var index = 0
//Question Label
#IBOutlet weak var questionLabel: UILabel!
//Answer Button
#IBAction func buttonAction(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerBox)) {
rightSeg()
print ("Correct!")
}
if counter != 0 {
counter = 15
questionTimer.text = String(counter)
} else if (sender.tag != Int(rightAnswerBox)) {
wrongSeg()
print ("Wrong!")
timer.invalidate()
questionList = []
}
}
override func viewDidAppear(_ animated: Bool) {
randomQuestion()
}
//variables
var counter = 15
var timer = Timer()
#IBOutlet weak var questionTimer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
}
Here's the code to the second view controller:
class ContinueScreen: UIViewController {
var delegate: QuizCompletedDelegate?
//correct answer label
#IBOutlet weak var correctLbl: UILabel!
//background photo
#IBOutlet weak var backgroundImage: UIImageView!
func backToQuiz() {
delegate?.continueQuiz()
if let nav = self.navigationController {
nav.popViewController(animated: true)
} else {
self.dismiss(animated: true, completion: nil)
}
}
#IBAction func `continue`(_ sender: Any) {
backToQuiz()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
How to make countdown timer reset after button push on different view
controller?
So if you can get the reference of yourView controller where you have
setup the timer then you can access counter variable from previous
view controller inside your second viewController and when pushed the
button just reset to 0.
If above approach is not possible for you then another way is create
the static variable and access it anywhere and reset like this:-
static var counter = 0
if you are popping/removing the 2nd view controller and thus returning to the first, you could reset the timer in your viewDidAppearfunc in your 1st view controller. this way the timer restarts every time the view appears - the first time, and whenever the 2nd pops.

swift property observer on reference property

How do you get a reference property to trigger a property observer?
In order to demonstrate my problem I wrote a simple MVC program with one button and one label. The button increments a counter in the model and displays the value of the counter in the label in the view controller.
The problem is that the counter increment (in the model) does not trigger the didSet observer ( in the view controller)
Here is the model file:
import Foundation
class MvcModel {
var counter: Int
var message: String
init(counter: Int, message: String) {
self.counter = counter
self.message = message
}
}
// create instance
var model = MvcModel(counter: 0, message: "" )
// counting
func incrementCounter() {
model.counter += 1
model.message = "Counter Value: \(model.counter)"
//print(model.message)
}
Here is the view controller file:
import Cocoa
class ViewController: NSViewController {
let model1 = model
var messageFromModel = model.message {
didSet {
updateDisplayCounterLabel()
}
}
// update Label
func updateDisplayCounterLabel() {
DisplayCounterLabel.stringValue = model1.message
}
// Label
#IBOutlet weak var DisplayCounterLabel: NSTextField! {
didSet {
DisplayCounterLabel.stringValue = "counter not started"
}
}
// Button
#IBAction func IncrementButton(_ sender: NSButton) {
incrementCounter()
print("IBAction: \(model1.message)")
}
}
I guess the problem is linked to reference property (as I have been able to make this program work with a model based on a struct).
I would appreciate if someone could tell me how to deal with property observers and reference property and make this kind of MVC work as I plan to use it in real programs.
You could create a delegate for MvcModel
protocol MvcModelDelegate {
func didUpdateModel(counter:Int)
}
next you add a delegate property to MvcModel
class MvcModel {
var counter: Int {
didSet {
delegate?.didUpdateModel(counter: counter)
}
}
var message: String
var delegate: MvcModelDelegate?
init(counter: Int, message: String) {
self.counter = counter
self.message = message
}
}
then you make the ViewController class conform to MvcModelDelegate and finally you set model.delegate = self into the viewDidLoad
class Controller: UIViewController, MvcModelDelegate {
let model = MvcModel(counter: 0, message: "hello")
override func viewDidLoad() {
super.viewDidLoad()
self.model.delegate = self
}
func didUpdateModel(counter: Int) {
print("new value for counter \(counter)")
}
}
In case someone is interested here is the code as suggested by Noam.
Model File:
import Foundation
protocol MvcModelDelegate {
func didUpDateModel(message: String)
}
class MvcModel {
var counter: Int
var message: String {
didSet {
delegate?.didUpDateModel(message: message)
}
}
var delegate: MvcModelDelegate?
init(counter: Int, message: String) {
self.counter = counter
self.message = message
}
}
// create instance
var model = MvcModel(counter: 0, message: "" )
// counting
func incrementCounter() {
model.counter += 1
model.message = "Counter Value: \(model.counter)"
}
}
View Controller File
import Cocoa
class ViewController: NSViewController, ModelDelegate {
// communication link to the model
var model1 = model
var messageFromModel = messageToLabel
override func viewDidLoad() {
super.viewDidLoad()
self.model1.delegate = self
}
// update display
func didUpdateModel(message: String) {
//self.Label1.stringValue = model1.message
self.Label1.stringValue = messageFromModel
}
// Label
#IBOutlet weak var Label1: NSTextField! {
didSet {
Label1.stringValue = " counter not started"
}
}
// Button
#IBAction func testButton(_ sender: NSButton) {
incrementCounter()
}
}