has no segue with identifier error iOS storyboard - swift

When I read the QR code, I want to open the viewcontroller with the webview. But I get the error in the title. Even though I have made the relationship between Viewcontroller and defined it, I still get this error. I defined it in web_load, but it still looks undefined.
func launchApp(decodedURL: String) {
if let url = URL(string: decodedURL) {
if UIApplication.shared.canOpenURL(url) {
// UIApplication.shared.open(url)
self.performSegue(withIdentifier: "web_load", sender: nil)
}
}
}) }
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "web_load"{
if let nextVC = segue.destination as? DetailsViewController {
nextVC.scannedCode = messageLabel.text
}
}
}

Make sure you have set "web_load" as a storyboard ID not as an identifier.
Follow this step
select the segue arrow -> got to attribute inspector -> identifier(web_load)
like this:

webload is segue identifier so that you can not set on viewController Storyboard ID you need to set as per given below screenshot.
Storyboard ID in need to set Your ViewController name.
Click on segue and set identifier as web_load then it can work properly.

Related

how do I refresh a childViewControler object when an action occurs on the parent viewControler

I have embedded a child ViewControler into my mainViewControler and it displays a graph.
the main view controller includes an input field and a save button.
I wish to have the graph in the childViewControler reload its data when the save button is taped.
the childViewControler includes a function
func refresh() {
fetchTransactionsAndCalculateTotals()
generateChartData()
setChart()}
that I would like to call. I have setup a segue from the main controller to the child controller,
enum Segues {
static let toInOutBar = "toInOutBars"
}
override func prepare(for segue: UIStoryboardSegue, sender: UIButton?) {
if segue.identifier == Segues.toInOutBar {
let destVC = segue.destination as! InOutBarViewController
destVC.refresh()
}
}
I am struggling to get further than this, I have tried to call this function from myUIButton action without success.
Thanks for any support
You should link an IBAction on the touchUpInside action of the button and get your child VC using the children property.
#IBAction func refreshClicked(_ sender: UIButton)
{
guard let vcChild = self.children.first as? InOutBarViewController else { return; }
vcChild.refresh()
}

Swift - Unwind Segue Pass Data Confusion

I've tried passing data backward from my unwind segue in a number of ways. It seems like the data is not getting sent or its getting sent after viewDidLoad() so the label I'm trying to set isn't getting updated. The unwind segue is working, and below I use prepare for segue with some success to change the title of the previous view controller to 'new title', but the last line isn't setting nbaRotoHome.player to 'new player name'.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "BuyStatsTapPager" {
let nav = segue.destination as! UINavigationController
let buyStatsTapPager = nav.viewControllers[0] as! BuyStatsTabPager
buyStatsTapPager.selectedPlayerBuyStats = selectedPlayer
buyStatsTapPager.buyStatsRef = self
}
if segue.identifier == "unwindToViewController1" {
var viewControllers: [UIViewController] = mainNavigationController.viewControllers as [UIViewController];
if(viewControllers.count == 3){
viewControllers.remove(at: viewControllers.count-2)
mainNavigationController?.viewControllers = viewControllers
}
let enteredContestViewController = viewControllers[viewControllers.count-1]
enteredContestViewController.title = "new title"
self.presentingViewController?.dismiss(animated: true, completion: nil)
let nbaRotoHome = segue.destination as! NBARotoHome
nbaRotoHome.player = "new player name"
}
Back in my previous view controller I have
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
}
And after looking at this question
Passing data with unwind segue
I've also tried getting the data this way in the previous view controller
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
if let sourceViewController = segue as? BuyStats {
playerNameLabel.text = sourceViewController.playerName
}
}
If I need to add more detail to what I'm trying to do please ask and I will edit. I wanted to ask the question but I am having trouble formulating.
It seems like the data is not getting sent or its getting sent after viewDidLoad() so the label I'm trying to set isn't getting updated.
In an unwind segue you are returning to an already created viewController, so viewDidLoad happened ages ago before you segued to the other viewController.
If you're using segues, you should not be mucking with the array of viewControllers in the navigationController or calling dismiss. The unwind segue will do all of that. Just get the destination in prepare(for:sender:) and set the data:
if segue.identifier == "unwindToViewController1" {
let nbaRotoHome = segue.destination as! NBARotoHome
nbaRotoHome.player = "new player name"
}
or in your prepareForUnwind get the source and read the data:
In this line you are missing .source. Change:
if let sourceViewController = segue as? BuyStats
to:
if let sourceViewController = segue.source as? BuyStats

How to use optional binding in switch statement in prepare(segue:)

In swift you can use a cool feature of the switch statement in prepare(segue:) to create cases based on the type of the destination view controller:
Example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case let detailViewController as DetailViewController:
detailViewController.title = "DetailViewController"
}
case let otherViewController as OtherViewController:
otherViewController.title = "OtherViewController"
}
}
However, what if the segue is triggered by a split view controller, so the destination is a navigation controller, and what you really want to do is switch on the class of the navigation controller's top view controller?
I want to do something like this:
case let nav as UINavigationController,
let detailViewController = nav.topViewController as? DetailViewController:
//case code goes here
Where I have the same construct that I use in a multiple part if let optional binding.
That doesn't work. Instead, I have to do a rather painful construct like this:
case let nav as UINavigationController
where nav.topViewController is DetailViewController:
guard let detailViewController = nav.topViewController as? DetailViewController
else {
break
}
detailViewController.title = "DetailViewController"
That works, but it seems needlessly verbose, and obscures the intent. Is there a way to use a multi-part optional binding in a case of a switch statment like this in Swift 3?
I worked out a decent solution to this problem.
It involves doing some setup before the switch statement, and then using a tuple in the switch statement. Here's what that looks like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let dest = segue.destination
let navTopVC = (dest as? UINavigationController)?.topViewController
switch (dest, navTopVC) {
case (_, let top as VC1):
top.vc1Text = "Segue message for VC1"
case (_, let top as VC2):
top.vc2Text = "Segue message for VC2"
case (let dest as VC3, nil):
dest.vc3Text = "Segue message for VC3"
default:
break
}
}
You might find this extension useful…
extension UIStoryboardSegue {
var destinationNavTopViewController: UIViewController? {
return (destination as? UINavigationController)?.topViewController ?? destination
}
}
Then you can simply…
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destinationNavTopViewController {
case let detailViewController as? DetailViewController:
// case code goes here
}
Note that the ?? destination makes sure the return value is non-optional, and also allows it to work in places where the destination could also be a non-navigation controller.
I don't think there is a way to do this with switch and case, but you can do something closer to what you are looking for with if and case (Update: as Hamish pointed out, the case isn't even needed for this scenario) or just normal if let:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let nav = segue.destination as? UINavigationController,
let detailViewController = nav.topViewController as? DetailViewController {
detailViewController.title = "DetailViewController"
}
if let otherViewController? = segue.destination as? OtherViewController {
otherViewController.title = "OtherViewController"
}
}
Since your switch statement in this example isn't really going to ever be verified by the compiler as handling all cases (because you need to create a default case), there is no added benefit to using switch instead of just if let
Optional binding doesn't really lend itself to switches like you're trying to do.
I understanding the desire to use switches rather than simple if and if else, but it's a bit different conceptually from what switch is meant to do.
Anyway, here are the two options I use for most situations
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case is DetailViewController:
segue.destination.title = "DetailViewController"
case is OtherViewController:
segue.destination.title = "OtherViewController"
default:
break
}
}
or
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = seuge.destination as? DetailViewController {
controller.title = "DetailViewController"
}
else if let controller = seuge.destination as? OtherViewController {
controllercontroller.title = "OtherViewController"
}
}
In my opinion, you should use segue identifiers for controlling flow in this switch. But to answer your question, this should work for you.
switch (segue.destination as? UINavigationController)?.topViewController ?? segue.destination {
...
}
The thing is, according to grammar, you have only one pattern (in your case it is binding) per item in a case item list. Since you have only one item on the input but want to use two patterns, you either want to normalize input (which is in this case appropriate as you can see above) or extend item list (which is inappropriate in this case but I show an example below).
switch ((segue.destination as? UINavigationController)?.topViewController, segue.destination) {
case (let tvc, let vc) where (tvc ?? vc) is DetailViewController:
// TODO: If you await your DetailViewController in navigation or without.
break
case (let tvc as DetailViewController, _):
// TODO: If you await your DetailViewController in navigation but other vc is not in a navigation vc.
default:
fatalError()
}

how to pass video field to AVPlayer controller from 'performSegueWithIdentifier' button, with swift 2.2

I have a 'performSegueWithIdentifier' set up to be called from a button. A URL for a video is in the string 'self.specimen.filmMovieLink'. How do I pass this field through to the the AVController? I have set up a segue called 'newMovie' and added the AV View Controller scene to the storyboard (without any code or attached file to control that scene).
I need to send it through from performSegueWithIdentifier("NewMovie", sender: specimenAnnotation)
If I add the URL and a direct string with a working URL it opens the AVPlayer Controller but does not play the video, when I tried this code:
performSegueWithIdentifier("watchMovie", sender: specimenAnnotation)
let url = NSURL(string: "https://www.example.com/video.mp4")
This code is working to segue the video URL to the AVController with my fields from a different view controller, using 'prepareForSegue' instead:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destination = segue.destinationViewController as!
AVPlayerViewController
let url = NSURL(string: self.specimen.filmMovieLink)
destination.player = AVPlayer(URL: url!)
}
}
I tried adapting it for the 'performSegueWithIdentifier' but am a beginner and couldn't get it to work. How can I adapt that code (or what new code do I need) to pass my field (self.specimen.filmMovieLink) to the AV controller with the 'performSegueWithIdentifier' instead of this 'prepareForSegue'?
The way I would approach it, I would create a property Observer:
// create a property observer that is set to nil then would change until something was assigned it to
var myURL:URL? = nil {
willSet(newValue) {
print("newvalue \(newValue)")
}
didSet{
// could do more stuff if needed it
}
}
Either in your ViewDidLoad or Inside of your UIButton action method your could call this line below
myURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
then
#IBAction func MyButton(sender:UIButton) {
performSegue(withIdentifier: "segueIdentifier", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
let destination = segue.destinationViewController as! AVPlayerViewController
guard let _url = myURL else {return }
destination.player = AVPlayer(url: _url)
destination.player?.play()
}
Voila, if you want to use tableView your could check out
my github project

prepareForSegue when embedding Tab Bar Controller into Navigation Controller

I currently have a navigation controller setup like this:
and my prepareForSegue, that passes data between the initial view (Login View Controller) and the Navigation controller looks like such:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
let navVc = segue.destinationViewController as! UINavigationController // 1
let chatVc = navVc.viewControllers.first as! ChatViewController // 2
chatVc.senderId = userID // 3
chatVc.senderDisplayName = "" // 4
}
However, when I try to embed in a Tab Bar controller (to add more pages/functionality to my app) like this...
...and run my application, my program crashes at the line let navVc = segue.destinationViewController as! UINavigationController
I know that the problem is that after my initial view, it goes to the tab bar which is type UITabBarController rather than UINavigationController however if I change it, my data does not go to the view that I want it to go to...it is kind of confusing.
Please let me know if you have any ideas how to implement this, or if you have any questions feel free to ask me for clarification.
Thanks!
P.s. The error that I am receiving in the console is:
Could not cast value of type 'UITabBarController' (0x10b8e48b0) to 'UINavigationController' (0x10b8e4860).
Try this:
Start by casting destinationViewController to UITabBarController and then using the viewControllers property to access the first viewController in the tabBarController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let tabVc = segue.destinationViewController as! UITabBarController
let navVc = tabVc.viewControllers!.first as! UINavigationController
let chatVc = navVc.viewControllers.first as! ChatViewController
chatVc.senderId = userID
chatVc.senderDisplayName = ""
}
In the CS193p class, Paul Hegarty shows the uses of extensions to deal with Navigation Controller segues (Lecture 8: 23'). The UIViewController extension introduces a new computed property: contentViewController available to all UIViewControllers (and subclasses).
The code I posted below was adapted to work with TabBarViewControllers as well.
When you are attempting to cast your navigation controller as your ChatViewController, the segue.destinationController.contentViewController will recursively return the ChatViewController.
class LoginViewController: UIViewController {
/* ... */
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let chatVc = segue.destinationViewController.contentViewController as? ChatViewController {
chatVc.senderId = userID
chatVc.senderDisplayName = ""
}
}
}
extension UIViewController {
var contentViewController: UIViewController {
if let navcon = self as? UINavigationController {
return navcon.visibleViewController ?? self
} else if let tabcon = self as? UITabBarController {
return tabcon.selectedViewController ?? self
} else {
return self
}
}
}
A clean way of doing this would be that right after you log in, you use something like:
let controller = self.storyboard?.instantiateViewControllerWithIdentifier("setthisisyourstoryboard") as! UITabBarController // This would instantiate the TabBarController.
let navInstance = controller[0] as! UINavigationController // This would instantiate the navigationController, that is placed at 0th index in the array of all view controllers that are child to TabBarController, since you only have one child, you can use 0 as index
if navInstance.viewControllers[0] is YourClassName { // YouClassName is the name of the class right next to the navigation view controller, and it is also the only child of navigation Controller(0 index)
// You can also send some data here (for example the sender id)
(navInstance.viewControllers[0] as! YourClassName).someProperty = Value
}
// This line would present the tabBar controller, that would ultimately reach the end of the stack. I have used this approach in many apps, it works great!
self.presentViewController(controller, animated: true, completion: nil)
You can delete the segue after this and set storyboard id for the tabbarcontroller.
I like to do it this way because it is more natural. What I mean is the UITabBarController is mostly a parent to View Controllers, I don't like the idea of setting a UITabBarController as a child to UIView Controller.
Maybe there is nothing wrong with it, but I don't prefer it.
Here. it's working pretty fine for me:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let tabVC = segue.destination as? UITabBarController {
if let navVC = tabVC.viewControllers!.first as? UINavigationController {
if let nextVC = navVC.viewControllers.first as? NextVC {
nextVC.varName = "works like a charm"
}
}
}
}
NextVC is your target VC which you want to send your variable into.