4

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 3enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

rowan atkinson
  • 119
  • 1
  • 10

2 Answers2

1

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.

Robert
  • 6,660
  • 5
  • 39
  • 62
  • Thanks for the answer, I will try this out too! – rowan atkinson Apr 16 '17 at 00:35
  • 1
    @johnrowland I hope it works for you! The accepted answer (using a global variable) will work in this case, but for a more complicated project you'll definitely want to know how to pass data directly between controllers. – Robert Apr 16 '17 at 01:01
  • I agree @Robert that this would be a good practice to learn. I've tried it but I think I'm a little confused on where I would be setting the Segue Identifier. I've added 3 images to my question, if possible, could you take a look and guide me on where I'd find the Segue Identifier and any other changes I should make? Is the Segue Identifier different than the Interface controller Identifier as seen in the 1st new picture? In that case, contextForSegueWithIdentifier() is probably returning the wrong value if I try to look for the Identifier "ThirdPage". – rowan atkinson Apr 17 '17 at 03:42
  • Also @Robert, if I use contextForSegueWithIdentifier(), how do I know which interface controller will be receiving the returned value? Would I receive it in the awake(withContext context: Any?) method? Thank you and sorry for all the questions – rowan atkinson Apr 17 '17 at 03:42
  • @johnrowland Oops, looks like I made a mistake. I thought you were using hierarchical navigation rather than page-based. For page-based navigation, apparently there is no way to pass info from one page to another, so you're stuck using globals like the other answer suggested. – Robert Apr 17 '17 at 04:35
1

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.

nathangitter
  • 9,607
  • 3
  • 33
  • 42
  • perfect, using a global variable worked great. Thank you! As you mentioned about avoiding global variables, could you explain the downsides to them? Would it be harder to submit an app using globals, or is validation of the variable to make sure it is not nil when unwrapping it? Thanks – rowan atkinson Apr 16 '17 at 00:34
  • Global variables are generally considered a bad practice because it's harder to maintain the software. When any object can change its value, it's really easy to introduce bugs in your program. It's also hard to determine which objects read and write that value from the code itself, so it can be harder to maintain long-term. – nathangitter Apr 16 '17 at 00:39
  • good point, that seems similar to reading a register's old value while a new value is being written to it. Thanks for the quick response! – rowan atkinson Apr 16 '17 at 00:43