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

Basically, what I'm trying to do is pass an integer from one interface controller. The slider in the controller "SecondPage" will get an integer value from the slider and should send it to the interface controller called "ThirdPage" and set the title of that label to the value. I've tried so many methods but all to no avail. I'm just trying to send the context from the 2nd interface controller, receive it in the awake(withContext context: Any?) method in the 3rd controller and save it as the title until the slider is changed again.
Here's what I tried first in the second page controller, but it didn't pass any data:
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
if segueIdentifier == "ThirdPage"{
return sliderval
}
return nil
}
here's what I tried after that, it passed the data but it doesn't "save" it. It pops up the interface controller with the updated label to the slider value, but I don't want it to pop up, I want that value to be saved to the label. When I close the pop up, and swipe right to the Third Page, the label is still called "Label"
self.presentController(withName: "ThirdPage", context: sliderval)
Here's some pictures of the output as well as some of my code. Any help is appreciated, thank you. PS: This is all in Swift 3

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.

Your undesired popup problem is happening because you're calling presentController() in sliderChanged(). Every time the slider changes, you're telling it to pop up the "ThirdPage" controller. You can just delete that.
Edit: My answer incorrectly assumed the asker is working with hierarchical navigation. For page-based navigation, there is apparently no way to pass data from one page to the next, so you're stuck with using a global as the accepted answer suggests. The original answer follows:
As for the lack of passing context, I would guess you probably don't have the segue identifier set in Interface Builder, or it's set to something other than "ThirdPage". The segue is separate from the "ThirdPage" controller, and you have to set its identifier separately. Click on the circle in the middle of the segue arrow and take a look at it in the Attributes inspector.
Assuming I'm right about that, the problem is that contextForSegueWithIdentifier() is being called with an identifier that you don't expect. Since it's not "ThirdPage", the conditional fails and it returns nil. Setting the segue identifier should fix it.

Related

How do I manually set the key-view in a cocoa app?

I have a cocoa app written in Swift. The app has a number of NSControls for user entries. The user can use the tab key to move between the controls, most of them being textfields. I am doing all in Code, I do not use the Interface Builder.
Some of them are dynamic: I have a custom NSControl, let's call it myRow which itself is hosting two ´NSTextFields´. myRow itself is hosted in a superView, let's call it myRowManager. When the user makes an entry this myRow notifies the myRowManager, and another myRow is placed below the first one. When one field's content is deleted by the user the myRowManager then deletes that myRow and re-arranges the remaining myRows. The whole setup is mimicking a table view, where there is always one more row than there are entries so that the user can always add an entry at the end.
Using the tab key when modifying existing entries works just fine.
But the nature of my setup means, that when a row is added or deleted a method .layoutSubviews() is called, there all myRows are removed from the superView (the manager) and then new myRows are created, reflecting the new number and order, which is of course reflecting the underlying data.
But by removing the myRow which just had the focus from the view hierarchy sets the window's initial responder as the key-view, as the current and next element in the key-view loop are no longer present.
I therefore am trying to set the key-view manually to the newly created textfield. To do this, a reference to this textfield is kept by the layoutSubviews method.
Unfortunately I have not been successful so far.
What I am doing now (this is the method of the manager which is called by a myRow when an entry is added):
func rowAddedAtEnd(_ index: Int) {
layoutSubviews()
self.window?.recalculateKeyViewLoop()
self.window?.selectKeyView(following:targetView)
}
What strikes me as odd is that there is no method th set a specific view as the key view. I can either use .selectKeyView(following:) or (preceding:)
But anyway - it does not work. The focus ring just gets reset to the top left element in the window.
How can I solve this?
From the documentation of func selectKeyView(following view: NSView):
Sends the nextValidKeyView message to view and, if that message returns an NSView object, invokes makeFirstResponder(_:) with the returned object.
To select targetView, skip nextValidKeyView and call makeFirstResponder(_:).
self.window?.makeFirstResponder(targetView)

NSSlider: how to send value and action at the same time?

I thought it would be simple, but seemingly it´s not.
I have a NSSlider from which the value I want to be shown in a label, like in this picture. With a setup as shown in this picture it´s working fine, using a binding takeIntValueFrom. Now I want this slider to set a new UserDefaults value:
#IBAction func sliderAction(_ sender: NSSlider) {
UserDefaults.standard.set(sliderOutlet.intValue, forKey: "sliderkey")
print(UserDefaults.standard.integer(forKey: "sliderkey"))
}
This actionfunc removes the takeIntValueFrom-binding. So in the end, it´s either or. Till now, I could not find a solution how to make it both work. Would be a pleasure to get some ideas.
Set the value of the label in sliderAction.
Or, for the more adventurous, bind (Bindings Inspector) the value of the slider and the value of the label to the Shared User Defaults Controller with the same key path. The action of the slider is not connected.

Swift IBoutlet default value

I have set an IBOutlet function for a segmented control. It will display a value only when a user changes the value, but I am wondering how would I be able to display the default value if the user does not select a different option.
For example, a segmented control with [First, Second, Third] would print Second when the user selects Second, but would not print First until the user selects something else and THEN First.
Very simple answer I'm sure, but I am new to Swift so please excuse me.
In your UIViewController viewDidLoad method you should be able to display the default value of the segmented control:
override func viewDidLoad() {
super.viewDidLoad()
myLabel?.text = segment.titleForSegmentAtIndex(segment.selectedSegmentIndex)
}
I think, you are looking for:
segmentedControl.titleForSegmentAtIndex(segmentedControl.selectedSegmentIndex)
which returns the title of currently selected segment.
I created a very simple test project demonstrating how that works.

Cannot use Add: action for NSTextField bound to NSArrayController

I have a view (created in a StoryBoard) that has a couple of NSTextFields and a slider control. I have one NSTextField that that takes the value of the another NSTextField and a sliderValue.
When I click the "Add" button (which is connected to the add: action of the array controller) all the values are added...except the value in the NSTextField with the "computed value". I can type in that field manually, and it works. Have looked on this site, apple documentation, and general google searches, but I'm not sure why I cannot add this data to the array controller. More details (code)...
NSTextField: Name **Gets added**
SliderValue: Grade **Gets added**
NSTextField: gradebookName **Only adds if manually typed in**
Code:
var txtGradeBookName: String
let sliderValueString = String(sender.integerValue)
txtGradeBookName = txtName.stringValue + "GradeBook" + sliderValueString
gradebookName.stringValue = txtGradeBookName
The ArrayController is bound to a Core Data model. Again, txtName and the Slider value gets added no problem...but gradebookName never gets added (unless I manually type it in)...and it's binding is set up identical to the the other two.

Navigation object

I have a navigation based application. when I click on a table's row next view appears and back button (manually created )appears. Now i want to know how can I get the object of previous view in current view so that I can change one of the label's text of previous view using previous view's object ?
Thanks.
You can pass the value before pushing the second view to the necessary variable
secondViewController.variable = firstViewController.variable;
Then push the secondViewController
Update:
Use a Bool variable willBePoppedBack as class variable and set it as NO initially.
- (void)viewWillAppear {
if(willBePoppedBack)
{
// your label text after pressing back button
}
else {
// your label's default text
}
}
You should set the variable willBePoppedBack to YES when you push to the next view.
If you want to do this you need to make a object of previous view here and then by . operator you can access first page label and write some thing on it.
FirstView *obj=[[[FirstView allo] initWithNibName:#"FirstView"] autorelease];
obj.label.text=#"Text";
Edit:
you need to make a NSString property in appDelegate class then make the object for app delegate and set the value of this string which you want on label
now pop the view and in viewWillAppear use this string to write on the label.
It must work.
Initialize the second view by passing the tableviews object, check the below link, with the similar approach you can achieve your requirement.
navigation based