15

I am working on a settings view controller screen for my iOS app written in swift. I am using a navigation controller to run the main settings table view which shows the cell titled, "Input Method." The current method is listed on the right of the cell. They can click the cell to go to the next view controller where they can select the input method that they'd like.

From here, there are two sections. The first is the input method to choose (touchscreen or joystick). The second section is joystick specific on whether or not the person is a lefty or righty. I don't want to have the vc unwind when they choose one box because they may choose one in another section too.

My question: How can I update the text field in the parent controller from the child controller.

Problems I'm having for optional solutions:

let parentVC: UIViewController = (self.navigationController?.parentViewController)!
parentVC.inputMethod.text? = cellSelected // This doesn't work because it cannot find the label inputMethod.

viewDidLoad() will cause a lag and the user sees the old method before it changes.

I cannot find out how to run a segue when someone clicks the back button at the upper left hand side in the navigation controller, since the navigation controller controls the segue.

AstroCB
  • 12,337
  • 20
  • 57
  • 73
Alec O'Connor
  • 245
  • 1
  • 3
  • 9

3 Answers3

51

It is not a good idea to cast the parent view controller, even when you are sure which class represents. I'll do it with a protocol:

In the child controller add a protocol like:

protocol ChildNameDelegate {
    func dataChanged(str: String)
}

class ChildClass {

    weak var delegate: ChildNameDelegate?

    func whereTheChangesAreMade(data: String) {
        delegate?.dataChanged(data)
    }
}

And in the parent:

class ParentClass: ChildNameDelegate {

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        guard let segueId = segue.identifier else { return }

        switch segueId {
        case "childSegue":
            let destVC = segue.destinationViewController as! ChildClass
            destVC.delegate = self
            break
        default:
            break
        }
    }

    // Child Delegate
    func dataChanged(str: String) {
        // Do whatever you need with the data
    }
}
javiazo
  • 1,892
  • 4
  • 26
  • 41
  • If my child view already has a delegate, I suppose this wouldn't work since I can't (easily) set another delegate. Is there any other way to achieve this? – SamAko Oct 24 '16 at 14:52
  • 2
    You can perfectly conform to more than one protocol (like when you conform to tableviewdelegate and tableviewdatasource). You have to name the delegate with another name (like 'var secondDelegate: SecondChildNameDelegate?') and set both delegates in the parent. – javiazo Oct 24 '16 at 15:06
  • Thats what i thought, just wanted to make sure, thank you very much. – Aragunz Mar 09 '18 at 18:47
  • I could only get this to work in Swift 4 by restricting the protocol to classes: `protocol ChildNameDelegate: class {` See this answer for more info: https://stackoverflow.com/a/24104371/7493938 – Mario Huizinga Aug 29 '18 at 17:45
1

You need to cast the parentViewController to whatever custom class it has. For example, if the parent has the class ExampleParentController, you would write:

let parentVC = (self.navigationController?.parentViewController)! as! ExampleParentController
parentVC.inputMethod.text? = cellSelected
thislooksfun
  • 1,049
  • 10
  • 23
  • 2
    Unfortunately this provides nil for the parent view controller, but I'm not sure why. – Alec O'Connor Feb 16 '16 at 18:28
  • 1
    @AlecO'Connor It might be because the parent is not from a navigationController. You could try something like let parentVC = parent as! ExampleParentController – C6Silver Apr 05 '17 at 05:33
0

I found a solution here: Modifing one variable from another view controller swift http://www.raywenderlich.com/115300/swift-2-tutorial-part-3-tuples-protocols-delegates-and-table-views

Instead of trying to access the view controller directly (which would be easier if it weren't returning a nil for the view controller) you can use a delegate method to adjust the variables. The delegate worked like a charm!

Community
  • 1
  • 1
Alec O'Connor
  • 245
  • 1
  • 3
  • 9
  • I still wonder why it's returning nil because if its just updating a label, accessing the parent view controller is much shorter and easier. – Eendje Feb 16 '16 at 19:18