Passing Data Between Interface Controllers in Watchkit

Passing data between interface controller in WatchKit

The issue is that you are passing a gameStruct instance using self.pushController(withName: "showDetails", context: gameWatchArray[rowIndex]) from your first interface controller to your second one, but then you are trying to cast gameStruct to String, which will obviously fail.

You should modify awake(withContext:) in your second interface controller to conditionally cast context to gameStruct and then access the gameName property of that struct when assigning the String name to the label's text.

In the future you should always handle the cases when a conditional casting fails so that you can find issues more easily by printing a message in case of a failed cast. Or if you are 100% sure that context will always be of a certain type, you can even do force casting, which will enable you to catch programming errors early on in the development stage.

override func awake(withContext context: Any?) {
super.awake(withContext: context)

if let gameDetails = context as? gameStruct {
gameNameLabel.setText(gameDetails.gameName)
} else {
print("Passed context is not a gameStruct: \(context)")
}
}

You should also conform to the Swift naming convention, which is UpperCamelCase for type names, so change gameStruct to GameStruct.

Why can't I send data between interface controllers for Apple Watch?

You are using a page-based navigation, and you need to pass data between pages.

What I would do is save the data from the slider somewhere that the third page can access, and populate the data in the label on willActivate. The willActivate method should be called by the system when the user swipes between pages.

At the global scope, create a variable to hold the slider value.

var sliderValue: Float = 0

In your second interface controller, set this value when the slider changes.

@IBAction func sliderChanged(_ value: Float) {
sliderValue = value
}

In your third interface controller, when the page is about to appear, set the label to the correct value.

override func willActivate() {
label.setText("\(sliderValue)")
}

If you want to avoid the use of global variables you can read and write from user defaults, or use other tactics, but this will depend on the exact needs of your application.

Pass array data from one interface to another in watchkit using pagebased navigation?

The WatchKit method for passing objects to a page-based WKInterfaceController is different than in iOS. While you will see a relationship segue on the Storyboard, when you click on it there is no option to name this segue (which is the first step to using the prepareForSegue: method in iOS).

Rather, what you do is pass one array that contains "context" objects, one of which will be provided to each WKViewController that is managing a page. In Objective-C:

+ (void)reloadRootControllersWithNames:(NSArray *)names
contexts:(NSArray *)contexts

In Swift:

class func reloadRootControllersWithNames(_ names: [AnyObject]!,
contexts contexts: [AnyObject]!)

If you have a object you want to pass from one to the other, you set that object as the context for each page:

NSArray * namesArray = @[@"Page 1", @"Page 2", @"Page 3"];
NSArray * contextsArray = @[myObject, myObject, myObject];
[self reloadRootControllersWithNames:namesArray contexts:contextsArray];

Somewhat counter-intuitively from the method name, while this method is called reloadRootControllersWithNames:, the WatchKit documentation on Managing Page-Based Navigation indicates that this same method should be used to seed these values at launch time, as well as any time you want to reload this data at runtime.

Passing data between two Interface Controllers

The best way to achieve this without using a global or a singleton (which is essentially a global for this case) would be to use the delegate pattern. In InterfaceController2 create a delegate that's of type InterfaceController1 and set it when the InterfaceController2 gets displayed. Then when you need the value you can call InterfaceController1Delegate.level to access the value.

You might be going about the problem wrongly though. From the sound of your names and values it seems like your holding state values for the app in the view controllers. If that's the case, I'd stick to a more MVC design and keep them in a state class or classes that get passed around or can be accessed from the various view controllers that might need them.

Unable to Pass Data Between Interface Controllers

To pass data between interface controllers you need add this method

pushControllerWithName:context:

Passing data back from a modal view in WatchKit

I wrote a full example that uses Delegation in WatchKit, passing the delegate instance in the context, and calling delegate function from the modal : Here is the full project example on GitHub

Here is the principale classes of the example :

InterfaceController.swift

This is the main Controller, there are a label and a button on his view. When you press the button, the presentItemChooser get called and it present the ModalView (ModalInterfaceController). I pass the instance of InterfaceController in the context to the modal. Important this controller implements `ModalItemChooserDelegate' functions (the protocol definition is in the modal file)

class InterfaceController: WKInterfaceController, ModalItemChooserDelegate {

@IBOutlet weak var itemSelected: WKInterfaceLabel!
var item = "No Item"

override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)

// Configure interface objects here.

}

override func willActivate() {
// This method is called when watch view controller is about to be visible to user
itemSelected.setText(item)
super.willActivate()

}

override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}

func didSelectItem(itemSelected: String) {
self.item = itemSelected
}

@IBAction func presentItemChooser() {

self.presentControllerWithName("ModalInterfaceController", context: self)

}
}

ModalInterfaceController.swift

This is the class of my modal controller. I hold the reference of my previous controller (self.delegate = context as? InterfaceController). When a row is selected, I call my delegate function didSelectItem(selectedItem) before dismissing it.

protocol ModalItemChooserDelegate {
func didSelectItem(itemSelected:String)
}

class ModalInterfaceController: WKInterfaceController {

let rowId = "CustomTableRowController"

let items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]

var delegate: InterfaceController?

@IBOutlet weak var customTable: WKInterfaceTable!

override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
self.delegate = context as? InterfaceController
// Configure interface objects here.
println(delegate)
loadTableData()
}

override func willActivate() {
// This method is called when watch view controller is about to be visible to user

super.willActivate()
}

override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}

private func loadTableData(){
customTable.setNumberOfRows(items.count, withRowType: rowId)
for(i, itemName) in enumerate(items){
let row = customTable.rowControllerAtIndex(i) as! TableRowController
row.fillRow(itemName)

}

}

override func table(table: WKInterfaceTable, didSelectRowAtIndex rowIndex: Int) {
let selectedItem = items[rowIndex]
self.delegate?.didSelectItem(selectedItem)
self.dismissController()
}

}

This is how I pass data back to my previous Controller. If is a better way let me know, I'll take it. :)



Related Topics



Leave a reply



Submit