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!
}
}
Related
I am using the following code snippet:
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
self.navigationController.pushViewController(secondViewController, animated: true)
I want to execute this code in the viewDidLoad. I will be using something like this in a series of if statements.
I used some code I found here that utilized a label. It did not work. Can a program move from one viewController without using a button? Almost every example, youtube video uses a button. I will be using a button when it is appropriate
You can not push view controllers before your current view controller loads. You should call your code in your viewDidAppear.
If you plan on directing to a particular view when the app launches I suggest you moving your logic to the AppDelegate in the method: didFinishLaunching. This way you don't need to unnecessarily load views.
Example of a ViewController being pushed in the viewDidAppear:
///
/// FUNCTION - VIEW DID APPEAR
///
override func viewDidAppear(_ animated: Bool) {
// GET VIEW CONTROLLER
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
// PUSH VIEW ONTOP OF CURRENT NAVIGATION CONTROLLER
self.navigationController.pushViewController(secondViewController, animated: true)
} // END - FUNCTION
The navigation controller is not the initial view controller. It is being instantiated in the initial VC and supplied with a root controller, the second VC. When I call present view controller function I cannot change the way in which this new VC in being presented, it always appears from the bottom. I did not find how to make it come from right to left. I tried to change who gets the call to modalPresentaionStyle from the second VC to the nav con and back but no change.
Code listing below. Thank you for your help.
#objc fileprivate func showRegisterScreen() {
let registerAccountVC = RegisterAccountVC()
let navigationController = UINavigationController(rootViewController: registerAccountVC)
navigationController.modalPresentationStyle = .pageSheet
present(navigationController, animated: true, completion: nil)
}
This will add a custom animation from right to left.
let transition = CATransition.init()
transition.duration = 0.4
transition.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
view.window?.layer.add(transition, forKey: nil)
present(navigationController, animated: false, completion: nil)
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.
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.
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()
}
}