Data not passing from main VC to embedded VC via prepareForSegue - swift3

I am trying to pass data from the main VC (called StartVC) to an embedded VC (called MPList - which itself contains a tableview).
The solutions I have found all point to using the prepareforsegue method but this does not seem to work for me. The app runs with no errors and the tableview is not getting populated with the data I send.
In StartVC, I have implemented the prepareforsegue method as follows (In the attributes inspector, the embed segue definitely has an identifier of "LeftPane".):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "LeftPane"{
let mpListVC = segue.destination as? MPList
mpListVC?.mpNames = self.mpNames
}
}
mpNames is a string array initialised in the embedded MPList as follows (mpListTV is the IBOUTLET for the tableview in question):
class MPList: UIViewController, UITableViewDataSource, UITableViewDelegate{
var mpNames = [String]()
override func viewDidLoad() {
super.viewDidLoad()
mpListTV.delegate = self
mpListTV.dataSource = self
print("names in MPList is: \(mpNames)")
}
The usual tableview control methods are implemented in MPList as well (and this is where mpNames is used too.):
//MARK: - TABLEVIEW METHODS
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return mpNames.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = mpNames[indexPath.row]
return cell
}
I have also made sure to right click and drag from the tableview to the little yellow icon at the top of the MPList viewcontroller and set both datasource and delegate.
I might just be tired (and missing something really obvious) after being at this for several hours now, but could someone please put me out of my misery and tell me what I am getting wrong?

Possible problems I see here:
1.
//in prepareForSegue
let mpListVC = segue.destination as? MPList
Make sure that the destination view controller in the IB is indeed set to MPList. Maybe mpListVC is nil here because of that. Just debug and make sure that mpListVc is not nil.
Also debug and make sure that no optional is nil. Better yet use force unwrapping (!) to try and find a possible nil somewhere.
Just set a breakpoint and check if mpNames is indeed set.
2.
viewDidLoad of MPList might be called before prepareForSegue for some reason, although it shouldn't. Either way it's a good practice to reloadData of the UITableView after resetting the array.
var mpNames = [String]() {
didSet {
mpListView?.reloadData()
}
}

Related

Pass variable to next View Controller

I want to pass a variable from one view controller to another.
First View Controller looks like this and the segue gets triggered correctly and the new view controller gets displayed.
#IBAction func testButton(_ sender: Any) {
let timerVC = TimerViewController()
timerVC.secondsPassed = textfield_seconds.text!
navigationController?.pushViewController(timerVC, animated: true)
}
On the next View Controller (TimerViewController) I declared a variable in the class header
var secondsPassed:String!
and in viewDidLoad I just want to print the value to the console and all I receive is a 'nil'.
I looked through various tutorials but I seem not to find the correct answer for this to get it work.
Anyone around with a clue?
Thanks in advance...
If you already are using a segue, you can remove this line that presents the controller (you don't seem to have a navigationController anyway):
navigationController?.pushViewController(timerVC, animated: true)
Now the correct way to pass data to another VC using segues is this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? TimerViewController {
vc.secondsPassed = textfield_seconds.text!
}
}

Calling a method when user leaves view controller inside container View

I am attempting to disconnect from a peripheral when ever a user leaves the current view controller. This would normally be easy by using prepare for segue in the following fashion:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
manager.cancelPeripheralConnection(peripheral)
}
This doesnt work, however; because the view controller is inside a container view. Does anybody know how to call this method when ever the container view is change from this view controller to a different one?
You could put it in func viewWillDisappear(_ animated: Bool) or func viewDidDisappear(_ animated: Bool). Both will get called when the user navigates away from the view controller for any reason.

Pushed View Controller Not Receiving Data

The Problem
I've sent data to other ViewControllers before but could not figure out why this situation was not working. The situation was as follows
FirstViewController and SecondViewController existed in different Storyboards
My FirstViewController would have some data that it needed to send to SecondViewController just before we programmatically pushed the SecondViewController. This was accomplished by:
#IBAction func someButtonPressed(_ sender: UIButton) {
let storyBoard = UIStoryboard(name: "SecondStoryBoard", bundle: nil)
if let secondViewController = storyBoard.instantiateInitialViewController() as? ResultsViewController {
secondViewController.var1 = self.var1
// Sanity check
print("set secondViewController.var1 to \(self.var1)") // Printed data as expected
self.navigationController?.pushViewController(secondViewController, animated: true)
}
}
My SecondViewController would test if the data was set correctly in its ViewDidLoad. This was accomplished by:
override func viewDidLoad() {
super.viewDidLoad()
if let var1 = self.var1 {
print("SUCCESS: var1 was set!")
} else {
print("FAILURE: var1 is nil! Crash imminent") // Printed here, hence my problem
}
}
Things I tried:
Creating getters and setters for var1 in SecondViewController and watching when they were being setted/getted. The variable was being set at the appropriate line by someButtonPressed in FirstViewController but by the time the if let var1 = self.var1 called the getter in FirstViewController, the value was nil.
Placing breakpoints on var1 in SecondViewController. I saw the variable be initialized to nil, as it is an optional, set and later set back to nil (again without calling the setter a second time)
Finding The Problem
The problem lies in the following screenshot of SecondViewController's View.
(Note: Let ResultsViewController = SecondViewController)
As you can see from the picture, this isn't the standard simple View you find in guides on how to pass data from one ViewController to another. My problem lied in the fact that we had one View embedding another. I did not realize, even though its listed in Xcode's interface builder, that embeds are a type of segues.
When was passing data to SecondViewController, I overlooked the outer View that was embedding the inner View. Both Views had their custom class set to SecondViewController, but that just masked the problem with the breakpoint situation described above.
What was happening was, I'd instantiate the outer View, set the data to its SecondViewController successfully and then it'd segue to its inner view and said inner view's SecondViewController would have the default nil values.
The Solution
To solve this problem I wrote a
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "secondViewController") {
if let secondViewController = segue.destination as? SecondViewController {
secondViewController.var1 = self.var1
}
}
}
in the SecondViewController so that when the Outer View segue'd to the inner, it'd pass its data along.
I also probably should have had both Views, inner and outer, have separate ViewControllers to avoid confusion.

How to find out the position of the contentViews inside tableView cell?

I placed a view inside a TableView Cell and have divided that view into 7 parts. Also, each view is assigned with a tag. Now, whenever I select a view, I want to find out the tag for identification and all I am getting is indexPath of the cell and details of all the views in it.
How can I find out the exact location or tag of the selected view? Don't want to use gesture. Can anyone help me out with this.
Thanks in advance.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var cell = tableView?.cellForRow(at: indexPath)
print(cell?.contentView.subviews)
}
What tag is that, you could get your cell properties normally after you got the cell.
if let cell = tableView?.cellForRow(at: indexPath) as? YourCellClass {
tag = cell.tag
}
print(cell?.contentView.subviews)

Automatic presenting View Controller

I have 2 view controllers on my storyboard: viewController1(it input point) and viewController2. After loading the application i want to automatic presenting viewController2 after viewController1.
How i can do that?
if you want to go to second viewcontroller you can add this code to you viewDidLoad method:
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "yourIdentifier") as! secondViewController
self.navigationController?.pushViewController(secondViewController, animated: true)
remeber to add a identifier for the second viewcontroller in storyboard and chenge the identifier that you use with "yourIdentifier"
if you don't want a animation put the animated: true to false.
if you dont want to show the fist viewcontroller, go into you storyboard and click on the second viewcontroller, then in the right side on attributes inspector select the box int the image:
For change viewcontroller you have to embed the first viewcontroller with a navigation controller, if you don't know how to do it, just select the fisrt viewcontroller and do like the image, click in navigation controller
Have you tried to show it in func viewDidLoad() {} like:
override func viewDidLoad() {
// ...
self.present(viewController, animated: true, completion: nil)
}
If you want to straight up load a different view controller you can do this in your app delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "put your identifier here")
self.window?.rootViewController = vc
return true
}