I have a storyboard with two main view controllers (ViewController / SecondViewController) and a container with an embedSegue that displays the EmbeddedViewController view.
Desired Result
I would like to pass the 5 from ViewController to EmbeddedViewController using prepareForSegue (I don't see a need for delegation here, since the data is only going 1 way, and that's TO the EmbeddedViewController.) Once "Segue" button is pressed, the second view controller appears with the yellow VC embedded with 5 in it.
Note - SecondViewController has no user interaction in displaying EmbeddedViewController. It just shows up automatically.
Problem
I can't get a reference to EmbeddedViewController from the mainSegue prepareForSegue. Nor can I access any child View Controllers from SecondViewController. Thought I could access properties of EmbeddedViewController if I access the children VC's of SecondViewController. See code below.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mainSegue" {
let secondVCSegue = segue.destinationViewController as! SecondViewController
print("array -- \(secondVCSegue.childViewControllers)")
// Array prints nothing. Just a blank [] :(
}
}
Any tips are appreciated thanks.
Related
I have UIPageViewController with 3 UIViewControllers ("FirstVC", "SecondVC", "ThirdVC").
ViewControllers changes by scroll, but I need to change its by click on UIButtons.
How I can do this?
Maybe some func, in which VC will setup by StoryboardID?
Thanks for all answers!
You can easily programmatically navigate through the pages of a UIPageViewController using:
setViewControllers([targetPage], direction: .forward, animated: true, completion: nil)
In the case where you have a UIPageViewController embedded in a ContainerView, and you want buttons in the "root" view to control the page view controller, the basic process is:
add navigation methods (funcs) to your page view controller class
save a reference to the page view controller when it is loaded have
your buttons call the navigation funcs using that reference
When your "root" view controllers loads and instantiates the view controller that is embedded in your ContainerView, it calls prepare(for segue:...) - which is where you get your reference.
In Storyboard, where you embed your view controller in the ContainerView, you will see a standard "segue" connection. In the Attributes Inspector, give that segue an Identifier, such as "PageControllerEmbedSegue".
In your root controller class, add a class-level var:
var myPageVC: BasicPageViewController?
and in prepare():
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// get a reference to the embedded PageViewController on load
if let vc = segue.destination as? BasicPageViewController,
segue.identifier == "PageControllerEmbedSegue" {
self.myPageVC = vc
}
}
Now, you can call functions / get properties of your embedded view controller.
I have a full example on GitHub: https://github.com/DonMag/EmbeddedPageView
There are a couple ways. You could add a segue in the Storyboard file and call
performSegue(withIdentifier: "toResponseTime", sender: self)
otherwise you could do something like.
let controller = self.storyboard!.instantiateViewController(withIdentifier: "AngelDetailViewController") as! AngelDetailViewController
self.navigationController!.pushViewController(controller, animated: true)
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 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.
I've been trying to pass data to another View Controller. But as I have two Bar Buttons that lead to two different View Controllers I have to set a condition. But when I try to pass the data it won't work. Here's the code I've been using:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ViewController") {
let destViewController = segue.destinationViewController as! ViewController
destViewController.chips = chips
destViewController.blind = blind
}
}
I have my destination Storyboard id to ViewController but when ViewController view opens the data isn't passed through.
EDIT:
In both View Controllers chips and blind are declared as:
var chips = 0
var blind = 0
Before I added a back button the data was passed correctly. But then the application crashed every time I clicked "Back" so I decided to add a condition which doesn't seem to work.
I'm very new to Xcode/Swift, but I believe the string in your if (segue.identifier == "ViewController") is the problem. Instead of "ViewController" you need to use the identifier of your segue. Select your segue in Main.storyboard, click on the Attributes Inspector and give it a name in the Identifier field. That's the string you want to use.