I have a TableViewController and 2 ways to get there.
one is a segue(show) from a Viewcontroller that is the root controller of a Navigation Controller, which itself is a tab of my Tab Bar Controller.
second, the tableVC is also a root VC of antoher Navigation Controller, that is also a tab in that Tab Bar Controller. Here is an illustration:
Now i want to check in the viewDidLoad if my TableVC, whether it is called by the first or by the second way. How can i find that out?
You could add a property to your view controller that indicates where it came from…
class MyTableViewController: UITableViewController {
enum Source {
case productList, basket
}
var source: Source!
}
then
override func prepareForSegue(segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? MyTableViewController {
vc.source = .productList
}
}
etc
very simple just add a var on your viewController which you are going to display , lets assume var vcOpenedBy = ""
now when launching this ViewController , just use this var and print whatever you feel comfortable like this
let vc = VcController(nibName:"",bundle:nil)
vc.vcOpenedBy = "NavigationMethod"
and at your segue you can also use this vcOpenedBy
and use string "SqgueMethod"
now on that viewController in viewDidLoad()
just print this vc . thats it
Related
Up to date Xcode/Swift/iOS.
I have a Master VC (called StartVC) that contains a Child VC (called TopBarVC) via and embedded segue. The Child VC contains a button, that, when pressed, modally segues to a 3rd VC (called CategoryPickerOverlayVC) (the view in this VC serves as a dropdown box for picking a category).
#IBAction func CategoryFilterButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "toCategoryPickerOverlay", sender: self)
}
When an option is selected from the dropdown box, which itself is composed of three buttons, the title of the selected button should be used to replace the title text of the button in the Child VC.
In the Master VC, I use prepareforsegue to store a reference to the Child VC in a variable - "topBarReference" - at the moment when the embed segue takes place.
var topBarReference: TopBarVC?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TopBarPane"{
topBarReference = segue.destination as? TopBarVC
}
}
Then, in the 3rd VC, when I click on one of the button options in the dropdown box, the button title is sent via a prepareforsegue to update the button in the Child VC (via "topBarReference").
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.filterButtonText = ((sender as! UIButton).titleLabel?.text)!
}
The 3rd VC then unwind segues back to the Master VC. I should add that when the button in the Child VC is changed, a variable (filterButtonText) in Child VC is first set with the title text and then this variable is then used to set the button title text via the viewDidAppear method of Child VC.
When using the debugger, I also note that viewDidAppear in the Master VC does not seem to execute after unwinding (I placed a diagnostic print-to-console in viewDidAppear and nothing prints after the unwind segue). I realise this would explain the button not getting updated but I've got no idea why viewDidAppear does not run.
I have also tried using a delegate protocol and instantiateViewController(withString:) to no avail. All of the methods produce the same result, which is that the button in the Child VC does not get updated. No errors are shown. Everything else happens as expected.
Any ideas as to what I am doing wrong?
Do you mean something like this?
If so, the solution I used was very simple: the third VC uses prepareForSegue to set a property of the embedded VC, and the embedded VC picks up that property in the unwind method.
In my implementation, the three view controllers are called ViewController, ChildViewController, and ThirdViewController. This is the entire code (everything else is configured in the storyboard):
class ChildViewController: UIViewController {
#IBOutlet weak var theButton: UIButton!
var buttonTitle : String?
#IBAction func unwind(_:UIStoryboardSegue) {
self.theButton.setTitle(self.buttonTitle, for: .normal)
}
}
class ThirdViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
(segue.destination as! ChildViewController).buttonTitle = (sender as! UIButton).currentTitle
}
}
Ok, so I have found that my original code works fine bar one line in the prepareforsegue of the Child VC. If I change that prepareforsegue from:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.titleLabel?.text = ((sender as! UIButton).titleLabel?.text)!
}
to this:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.setTitle((sender as! UIButton).titleLabel?.text, for: .normal)
}
it works just fine. The use of the .setTitle method seems to make a difference although I am not sure why.
Thanks to Matt for giving me the idea to change it to that. Matt's method did work when i tried it, although, as I am unwinding to the Master VC and not the Child VC, I had to edit the code accordingly, in terms of where I placed it.
As my little "discovery" equates to the smallest change to the original code, I'll mark this as the answer.
Thanks to all for taking the time to respond!
I am developing in Swift.
And the following picture is my storyboard.
There has a Main view. The Main view will change the view to the Scan view and also pass the data to the Scan view when press the Scan (Right Bar button item).
And the identifier of the StoryBoard Segue is ScanView
I use the following code to pass the data from Main to the Scan
When press the Scan (Right Bar button item).
self.performSegueWithIdentifier("ScanView", sender: self)
And pass the data to the next view
//prepare jumping to next page
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier=="ScanView"){
let desViewController = segue.destinationViewController as! ScanViewController
desViewController.myCenteralManager = myCenteralManager
}
}
And it will crash at let desViewController = segue.destinationViewController as! ScanViewController and show the error like the following :
Could not cast value of type 'UINavigationController' (0x3960e0a8) to 'BLEConnect.ViewController' (0x5514c).
Can someone teach me how to solve the issue ? Thanks in advance.
The error message are pretty clear, you try to get segue.destinationViewController as ScanViewController while in fact it is a navigation controller. You need to get the navigation controller first and then use its topViewController property to get your targeted view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier=="ScanView") {
if let navController: UINavigationController = segue.destinationViewController as? UINavigationController {
if let desViewController: ScanViewController = navController.topViewController as? ScanViewController {
desViewController.myCenteralManager = myCenteralManager
}
}
}
}
Your NavigationController is (probably) in the wrong place in Interface Builder. Right now, when you segue, you are going to the UINavigationController and not the ScanViewController like you expect. This is why the cast fails, because you are trying to force the UINavigationController to be a ScanViewController.
To fix this, you should place your MainViewController in the UINavigationController and then segue straight to your ScanViewController.
This guide shows exactly how you can use Interface Builder, UINavigationController, and segues to achieve what you're trying to do.
You can embed your navigation controller to the main ViewController and perform a segue as you have done above. That should solve the problem.
The error message is displayed because you have type casted Navigation Controller instead of Scan ViewController .
I have 2 controllers: ViewController and GalleryViewController(with the collection view on it). From the storyboard I set for the collectionView in GalleryViewController Scrolling Enabled to false. Now, how can I change it to true from the ViewController?
I've tried this:
var vc: GalleryViewController?
vc.collectionView.scrollEnabled = true
but it does not work. Is there another solution with which I can change scrolling from another controller(ViewController)?
your code will create a new instance of GalleryViewController and you need to use the existing one.
You have a number of options, partly depending on how you navigate from ViewController to GalleryViewController.
If you are creating the Gallery View from your initial controller, then you should use prepareForSegue, something like this
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if segue!.identifier == "GallerySegueOrWhateverYouHaveCalledIt" {
let viewGalleryController:ViewGalleryController = segue!.destinationViewController as ViewGalleryController
let collectionViewLink = viewGalleryController.collectionView
}
}
If you're using a Tab Controller, and assuming you know the index of your GalleryView, let's call it indexGalleryView, then it's even easier
var vc = tabBarController!.viewControllers![indexGalleryView] as! GalleryViewController
vc.collectionView.scrollEnabled = true
And if you have a ViewController -> Container -> Embed GalleryViewController -> CollectionView, you can get a handle to the embedded ViewController in the viewDidLoad of the top level controller like this
for vc in self.childViewControllers
{
if vc.isKindOfClass(GalleryViewController)
{
myGalleryViewController = vc as! GalleryViewController
}
}
once you have myGalleryViewController you should be able to access everything on the child view
I have two UICollectionViewControllers and the first one uses a push segue to get to the second one. The problem I'm having is passing information back to the first controller when the back button (the one that gets added automagically) is pressed in the second controller. I've tried using the segueForUnwindingToViewController, and canPerformUnwindSegueAction override functions, but no dice. I need to be able to access both view controllers so I can set some variables. Any ideas?
Here is an example with two view controllers. Let's say that the names of the two view controllers and ViewController and SecondViewController. Let's also say that there is an unwind segue from the SecondViewController to the ViewController. We will pass data from the SecondViewController to the ViewController. First, let's set the identifier of this segue by opening the document outline and selecting the unwind segue. Then open up the attributes inspector and set the identifier to "unwind".
SecondViewController Code:
class SecondViewController: UIViewController
{
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if let destination = segue.destinationViewController as? ViewController {
if identifier == "unwind" {
destination.string = "We Just Passed Data"
}
}
}
}
}
ViewController Code:
class ViewController: UIViewController {
var string = "The String That Will Be We Just Passed Data"
#IBAction func unwindSegue(segue: UIStoryBoardSegue) {
}
}
It sounds like you are trying to intercept the back button, there are many posts for this on SO, here are two:
Setting action for back button in navigation controller
Trying to handle "back" navigation button action in iOS
In practice, it is more clear to return state in closures (more modern), or delegates.
Background: I want to display a modal segue from a UITableViewController(A) to a UITableViewController(B), but I want to show a NavigationBar to "Cancel" and "Save".
What I've done:
In storyboard:
I ctrl drag the cell from A to B, and set segue identifer "selectItem"
I choose B and select "Editor - Embed in - Navigation Controller"
In A's ViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifer == "selectItem" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let destinationViewController = segue.destinationViewController as B
// Pass value from A to B
}
}
}
Error:The app crashed at let destinationViewController = segue.destinationViewController as B with the error swift_dynamicCastClassUnconditional. If I didn't embed a navigation controller to B, the program would not crash. But I really need a navigation bar.
Is there any solution or other way to achieve this? Thank you!
PS: I tried drag a NavigationBar from object library in storyboard, but it's miss a part of background to cover statusbar...
Create both of the UITableViewController in your storyboard.
Select your Second UITableViewController (the one you want to present modally), and embed it in an UINavigationController.
Add the "Cancel" and "Save" UIBarButtonItem into UINavitionItem of the Second UITableViewController.
Select the UITablViewCell of your First UITableViewController
Control+Drag into your UINavigationController.
Select "Present Modally" under the "Selecteion Segue" option from the dropdown list.
To pass data in your First UITableViewController override the method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "identifier" {
let destination = segue.destinationViewController as UINavigationController
let bViewController = destination.topViewController as BViewController
// pass data
}
}
Here are the screenshots:
This should do the job, Cheers!
I solved it!
I added a BreakPoint at the line let dest = segue.destinationViewController as B, and I found that segue.destinationViewController is NavigationController type. So I fixed it by replacing this line to:
let dest = segue.destinationViewController as UINavigationController
let bVC = dest.topViewController as B
and do some passing value stuff.
Hope this will help other people facing this problem.
I use
if let b = segue.destinationViewController.childViewControllers.first as? B {
//do something with b
}
for this scenario. It is really just a syntax difference though.