I am woking on an recording APP.
I tried to add navigation controller in my first recording viewcontroller which then could pass filename array to the second viewcontroller using the following function prepare:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let controller = segue.destination as? MainViewController
controller?.recordArray = recordingArray
self.present(controller!, animated: true, completion: nil)
}
However, when ran in the simulator the navigation bar disappeared in the second controller and Xcode pops out the warning
Thread1: EXC_BAD_ACCESS(code=2,address=0x7fff51edfff8)
Has anyone got any advice?
Thanks!
Don't ever call present(_:animated:completion:) inside prepare(for segue:).
prepare(for segue) is called automatically by the system just before a segue is about to happen to let you prepare the data for sending to the destination viewcontroller or do any other calculations you need to before performing the segue. A segue needs to be set up in Storyboard and it will either be called automatically or if it is a manual segue, you need to call it using perform(segue) and once you do that, the system will call prepare(for segue) for you. You can see why calling another navigation function proves to be problematic, since you are trying to navigate to another viewcontroller using two different methods (segue and present).
If you haven't set up the segue in Storyboard, then you also need to do that, since if it is not set up, let controller = segue.destination as? MainViewController will be nil.
Once you set up the segue in Storyboard, this is how your function should look like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? MainViewController {
controller.recordArray = recordingArray
self.present(controller, animated: true, completion: nil)
}
}
You shouldn’t be trying to present a VC in this method, it’s just a place for you to configure the destination VC before the segue presents it
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let controller = segue.destination as? MainViewController else { return }
controller.recordArray = recordingArray
}
Related
my problem is that when i switch to an another ViewController, my variables of the previous VC call are reset so i can't do what i want after
#IBAction func BackBtn(_ sender: Any) {
self.nbrQst = 10
self.Switch1A = 2
performSegue(withIdentifier: "Numero", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Numero" {
let vc = segue.destination as! ViewController
vc.nbrQt = nbrQst
}
if segue.identifier == "Numero" {
let vcNv = segue.destination as! ViewController
vcNv.Switch1 = Switch1A
}
}
below the way that i send information from my Lvl1 file to the lvl selector file to add a if else for unblocks the lvl
#IBAction func BackBtn(_ sender: Any) {
self.nbrQst = 10
self.Switch1A = 2
performSegue(withIdentifier: "Numero", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.nbrQt = nbrQst
vc.Switch1 += 2
}
that's the second ViewController who send the number to the first one, and i want that the first one remember the number and add them up each time I press the button that sends the numbers
Let's call "Selector VC" the main VC.
The reason why those numbers reset in main VC is that when you press the "BackBtn", you're not actually going back to main VC, you're creating a new main VC and going there (because you did performSegue(withIdentifier: "Numero", sender: self) in BackBtn function, performSegue always takes you to a new VC rather than taking you back). The new main VC has all those fields starting from an initial number (I assume initially they're zero).
What you want to do is to go BACK, not forward. Depending on how you went to the level VC from main VC, you have different ways of going back to main VC.
If you're pushing everything onto a navigation controller, then you can go back by:
self.navigationController?.popToRootViewController(animated: true)
If you're presenting everything modally, you can go back by
self.dismiss(animated: true, completion: nil)
You said, you want to update the value in main VC so that you can block/unblock next level. A very standard way to achieve this is via delegate, please Google this ("delegates in Swift") and follow their tutorials there.
I strongly recommend that you take an online course on swift development before you go ahead and develop your own app, because you could potentially do a lot of things wrong and waste a lot of your time trying to get an answer from Stack Overflow.
I have 2 buttons in a view controller. If button 1 is clicked, then go to WebView controller. If button 2 is clicked then go to ArtView controller.
How to make override func prepare(for segue: UIStoryboardSegue, sender: Any?) have 2 destinations depending on the button clicked?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? WebViewController {
// your code
}
if let destination = segue.destination as? ArtViewController {
// your code
}
}
Hope it will help! :)
Have each button invoke a different segue, either by control-dragging segues directly from the button, or by calling performSegue(withIdentifier:sender:) from your buttons' IBAction code.
Then use code in prepare(for:sender:) to figure out which destination is being invoked.
You can:
Check the identifier on the segue (not recommended - fragile)
Check the class of the destination view controller (better than 1)
Have each view controller implement a protocol and check the protocol of the destination:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case let webView as WebViewProtocol:
//Your code for a web view
case let artView as ArtViewProtocol:
//Your code for an Art View
}
}
Option 3 is reliable while providing loose coupling (each case can trigger any view controller that conforms to the desired protocol. All it needs to know is that the destination understands the desired protocol.)
I'm using a Page View Controller to implement an image carousel in my app. I have a container view in my parent view controller which has an embed segue to the Page View Controller. The problem I'm having is when I need to go off and fetch these images asynchronously and the segue happens before viewDidLoad gets the images.
My prepare for segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "startCarousel" {
let carouselPageViewController = segue.destination as! CarouselPageViewController
carouselPageViewController.images = carouselImages
}
}
in viewDidLoad:
GalleryManager.sharedInstance.getData = {
(data) in
// Assign images to instance property here ...
}
My thinking is that once I get the images that I programmatically start the segue myself. Or if I should be doing this a different way any advice would be much appreciated.
You can do something like, return false in shouldPerformSegue and initiate segue when data is loaded
i.e.
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "startCarousel" {
return (detail != nil)
}
return true
}
Then in viewDidLoad
GalleryManager.sharedInstance.getData = { [weak self]
(data) in
self?.performSegue(withIdentifier: "startCarousel", sender: nil)
}
No, you can't delay an embed segue. If you use an embed segue it fires as soon as the view controller is loaded.
You need to wait until the data loads and then call the setViewControllers(direction:animated:completion) to install the view controllers that you will use to display the data once the data is loaded.
Swift 3, Xcode 8
You can realize in this way:
set a reference to your page view controller in prepareForSegue in your masterViewController:
var myContainerViewController: UIPageViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "embedSegue" {
if let destination = segue.destination as? YourPageViewControllerClass {
self.myContainerViewController = destination
}
}
Define a function in your own pageViewController class:
func someFunction(){
//configure your image or swipe page
}
when you get the images, in your masterViewController call:
self.myContainerViewController?.someFunction()
you can't delay an embed segue.
you try to put button in view controller and then after view load set button click to set image...
I have a tabbed view controller that is associated with two view controllers.
I need to pass an array from the first View Controller to the Second View controller.
In Order to do this I have the following code in my first VC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "AddItem"
{
if let destinationVC = segue.destination as? SecondViewController
{
destinationVC.toDoList = toDoList
}
}
}
However this is not getting fired when I switch two the second VC using the tab button.
Any ideas as to why this is not getting fired?
This is how my main storyboard looks like:
Main Storyboard
You cannot transfer objects between tab views through segue because the VC is not actually making a direct segue connection rather you can do it with a delegate or a notificationCenter post and observe to transfer
You have to put that segue code in Tabbar controller class. You shouldn't put that thing in firstVC. Because there is no segue is going on from first VC to Second VC.
I am using for the first time the Unwind Segue approach. I have multiple view controllers as can be seen in the picture below (a few of them shown of course). In the Edit Profile I have a Done button. Once clicked I trigger an IBAction that triggers an unwind segue.
Here is the code for the Done button in the nav bar:
#IBAction func unwindToMainViews(sender: UIStoryboardSegue) {
//let sourceViewController = sender.sourceViewController
self.performSegueWithIdentifier("unwindToMainSegue", sender: self)
}
and in the
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
I am doing the following to pass data from Edit Profile back to Home View
if (segue.identifier == "unwindToMainSegue") {
// pass data to next view
let tabCtrl = segue.destinationViewController as! UITabBarController
let destinationVC = tabCtrl.viewControllers![0] as! HomeViewController
destinationVC.userObject = self.userObject;
}
When segue identifier is matched and code is executed (to transfer userObject from this controller to another), it triggers the error:
Could not cast value of type 'app.EditProfileViewController' (0x100b99d80) to 'UITabBarController' (0x104a1d030).
How can this error be fixed? I am surprised since i am casting to UITabBarController so thought it should work.
You don't return to the UITabBarController in an unwind segue. You return to the ViewController that triggered the original segue, or one if its ancestors.
As #jlehr stated in the comments:
Unwind segues don't return to anything; they dismiss any pushed and
presented view controllers between the source and destination view
controller. The destination is wherever the implementation of the
unwind method is found, regardless of how the source view controller
was presented.
To unwind to the viewController that triggered the original segue, you need to implement the #IBAction func returnToHere(segue: UIStoryboardSegue) function in the viewController you want to return to.
Then when you set up your Done button by dragging to the Exit icon, you select returnToHere from the pop-up.
To pass data back to the sourceViewController, give your unwind segue an identifier such as "unwindToSource", then override prepareForSegue in the viewController you are returning from:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "unwindToSource" {
let dvc = segue.destinationViewController as! SourceViewController
dvc.userObject = self.userObject
}
}