Segue not working - swift

I am making an iOS app in swift where I have a tableView with cells, what I want is to transit to another view controller when I click on the cell. Here is my code block for the segue source.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "selectedEvent"{
let cellIndex = tableView.indexPathForSelectedRow();
let tempIndex = cellIndex?.row
let name = eventsManager.createdEvents[tempIndex!].name
let address = eventsManager.createdEvents[tempIndex!].address
let latD = eventsManager.createdEvents[tempIndex!].eventLat
let lonD = eventsManager.createdEvents[tempIndex!].eventLon
let coords = CLLocationCoordinate2DMake(latD, lonD)
}
println("segue fired")
}
Here is the code in the segue destination.
#IBAction func showOnMap(segue: UIStoryboardSegue){
println("segue check")
self.performSegueWithIdentifier("selectedEvent", sender: self)
let showEventController = segue.sourceViewController as EventListViewController
let focusAddress = showEventController.address
let position = showEventController.coords
let name = showEventController.name
var marker = GMSMarker(position: position!)
println("is marker working? I hope so")
marker.title = name
marker.map = self.mapView
}
The println statements are to check if the segue is firing and the first statement "segue fired" prints on the console, and the view in the simulator switches to the destination viewController. Unfortunately, the 2nd part of the code does not execute and I am having trouble figuring out why.

The println("segue check") line will only print if the code inside IBAction is triggered by an event. Make sure IBAction is connected to the proper object in your view controller's view.

Why are you calling self.performSegueWithIdentifier("selectedEvent", sender: self) at the destination view controller's showOnMap function and then appear to be getting an instance of the view controller where you came from?
If you want to save data in your segue destination, you should first define variables in your destination's view controller and then save it at the destination in your prepareForSegue above. Something like this:
if segue.identifier == "selectedEvent"{
...
let coords = CLLocationCoordinate2DMake(latD, lonD)
...
let myDestVC = segue.destinationViewController as MyDestinationViewController
let myDestVC.coords = coords
...
}
Once you've got all your data saved, you can use them where ever you want in your destination's view controller. If you want to use them immediately, you can do so by overriding viewDidLoad viewWillAppear or viewDidAppear,

Related

iOS QR code scanner triggering actions multiple times

I am using the QR code reader code from AppCoda (http://www.appcoda.com/qr-code-reader-swift/#comments) and converted it to Swift 3. The base code works perfectly fine.
However, what I want to achieve is to retrieve a String from the QR reader, store it in a variable, and pass the variable to a following view controller. So I added a bit of code to it to get the following:
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
messageLabel.text = "No barcode/QR code is detected"
return
}
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if supportedBarCodes.contains(metadataObj.type) {
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
messageLabel.text = metadataObj.stringValue
}
//
//
// CODE I ADDED STARTS BELOW
//
performSegue(withIdentifier: "showMenu", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showMenu"{
let tabVc = segue.destination as! UITabBarController
let navVc = tabVc.viewControllers?.first as! UINavigationController
let menuVc = navVc.viewControllers.first as! MenuViewController
menuVc.qrScan = self.messageLabel.text
}
It actually achieves what I wanted it to do, but it somehow triggers the "performSegue" multiple times. The transition animation of the first segue goes halfway, and then the second one happens.
The segue "showMenu" is a manual segue connected from the view controller of the QR Reader view to a tab bar controller that houses a navigation controller and Menu view controller.
Things I've tried:
Print the passed on variable in the viewdidload function of Menu view controller. The string gets printed out twice.
Added a hidden button on the QR reader view and change the segue connection of "showMenu" from the view controller to the button. Removed perform segue from code. While holding the camera in place to scan qr codes, pressing the button performs the intended function and the segue is only triggered once.
With the same setting as (2), I programmatically trigger the button by using a touch up inside event when a qr code is scanned. The segue gets triggered twice.
Added breakpoints for performSegue and the if clause right above it. After I scan something, and press the "continue program execution" button, the program loops between the two breakpoints on and on.
If anyone can enlighten me a bit over here, I would be very grateful. Thanks a bunch for the help in advance.
The issue was fixed by adding the following line of code after the segue was performed:
self.captureSession?.stopRunning()
However, the question of why doesn't unwind segue have a similar issue still remains.
I've just solved the same problem. You have to create an unwind to the previous view.
To do this in the interface builder, you have to ctrl-drag from the controller to the exit in the controller you're actually in (not the one you want to unwind).
Once you have done this, you have to set an identifier to this unwind segue (in your case "showMenu". To do this, you have to the unwind segue from the scenes menu on the left part and set the identifier name on the Attributes inspector.

Variable shared between view controllers of storyboard?

My app is split into 2 storyboards: a character selection one and a main application one. When the user selects a character the app segues's to the main application, and all the views of that storyboard should now relate to the character the user selected.
I'm trying to find out the best way to share a String that will have the selected character's information between all the main application storyboard's views. Right now I'm using UserDefaults to just set a global variable:
func loadMainApp(sender: UITapGestureRecognizer) {
let currentCharcter = allCharacters[(sender.view?.tag)!]
let defaults: UserDefaults = UserDefaults.standard
defaults.setValue(currentCharacter, forKey: "CurrentCharacter")
performSegue(withIdentifier: "MainAppSegue", sender: self)
}
From there all the view controllers in the Main App storyboard can fetch the string from UserDefaults.
Is this the best way of doing such a thing or is there a better way?
The better way is to pass the character to the viewController you are segueing to, and the easiest way to do that is with prepare(for segue. If you change your performSegue call to pass the sender on by saying performSegue(withIdentifier: "MainAppSegue", sender: sender) you will be able to access that in prepare(for like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//Safely check to make sure this is the correct segue by unwrapping the sender and checking the segue identifier
if let tapGesture = sender as? UITapGestureRecognizer, let tapGestureView = sender.view, let mainViewController = segue.destination as? MainViewController, segue.identifier == "MainAppSegue" {
//Get a reference to the character that was selected
let currentCharacter = allCharacters[tapGestureView.tag]
//Pass the character to the new viewController
mainViewController.character = currentCharacter
}
}
I made a couple assumptions about the name of the viewController you are performing the segue to, and assumed it has a variable called character where you can send your character. Your new viewController now has a reference to the character.
If I understand you well. Personally I am using Singeltons for achieving Global variable between Views rather than UserDefaults
class SomeClass{
static let sharedInstance = SomeClass()
var someString = "This String is same from any class"
}
Usage inside of some function :
SomeClass.sharedInstance.someString = "Changing Global String"

How do I display the data fetched from called view controller into a dynamic tableviewcell of the calling view controller while using unwind segue.?

I have dynamic tableview, wherein one of the cell (duration) when tapped opens another view controller which is a list of duration viz (30 min, 1 hour, 2 hours and so fort). One of the durations when selected should display the selected duration in the first view controller. I am able to pass the data back to first view controller using unwind segue but unable to display the passed value. DOn't know whats missing.
I am displaying the code below:
FIRST VIEW CONTROLLER (CALLING)
#IBAction func unwindWithSelectedDuration(segue:UIStoryboardSegue) {
var cell = tableView.dequeueReusableCellWithIdentifier("durationCell") as! durationTableViewCell
if let durationTableViewController = segue.sourceViewController as? DurationTableViewController,
selectedDuration = durationTableViewController.selectedDuration {
cell.meetingDurationCell.text = selectedDuration
duration = selectedDuration
}
SECOND VIEW CONTROLLER (CALLED)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SaveSelectedDuration" {
if let cell = sender as? UITableViewCell {
let indexPath = tableView.indexPathForCell(cell)
if let index = indexPath?.row {
selectedDuration = durationList[index]
}
}
}
}
tableView.dequeueReusableCellWithIdentifier should only be called within tableView:cellForRowAtIndexPath:. It has no use outside this context.
The easiest fix is to just reload the table once you have stored the selected duration:
#IBAction func unwindWithSelectedDuration(segue:UIStoryboardSegue) {
if let durationTableViewController = segue.sourceViewController as? DurationTableViewController {
selectedDuration = durationTableViewController.selectedDuration
tableView.reloadData()
}
}
Note that this assumes you only need one selectedDuration for your whole table, rather than one per row. If you need one per row, I assume you have them stored in an array somewhere, so it is that array that should be updated instead before the reloadData.

Making a prepareForSegue wait till after a Realm database write is completed

In my program when a button is pressed I am adding information to a database, including creating invoice number then calling a segue to a new view controller. When the new view controller is called I'd like to pass along that invoice number. Everything works fine, I can pass along sample data no problem. However, it appears that "override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject!) {}" is being called before my button (upon initialization of the view controller?), so I am passing along a blank value. How can I make my prepareForSegue wait till after my button is pressed? Here is the code I currently have.
#IBAction func createInvoice(sender: AnyObject) {
let realm = Realm()
let invoicepull = Invoice()
let invoicecount = realm.objects(Invoice)
let invoicenraw = invoicecount.count
let a = 100
let invoicenumber = a + invoicenraw
var invoicefile = Invoice()
invoicefile.inumber = invoicenumber
invoicefile.cnumber = clientcombo.stringValue
invoicefile.cost = owed.doubleValue
invoicefile.paid = paid.doubleValue
invoicefile.sevicecode = service.stringValue
invoicefile.dateofservice = NSDate()
// Save your object
realm.beginWrite()
realm.add(invoicefile)
realm.commitWrite()
//Sent notification
performSegueWithIdentifier("cinvoiceseuge", sender: nil)
println("Inside Action")
println(invoicenumber)
dismissViewController(self)
}
override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "cinvoiceseuge") {
//Checking identifier is crucial as there might be multiple
// segues attached to same view
var detailVC = segue.destinationController as! invociegenerator;
detailVC.toPass = invoicenumber
println("Inside Sugue")
println(invoicenumber)
}
}
Update: I belive this is an issue with the Realm database causing it to behave unexpectedly. If I remove all realm code, the program works as expected and I can pass a static dummy value.
invoicenumber in createInvoice() is a local variable and invoicenumber in prepareForSegue() seems to be an instance variable. is it what you expected?

How do I pass a value from my SplitView controller to a map?

So I have a SplitView controller and the cells have cities and states (ex: Dalllas, TX, etc) when I select a cell I know how to go to a detail view using a segue. But, how do I send data another UIViewController containing a map?
After I’ve created my segue my code associated with the SplitView looks like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let cell:UITableViewCell = sender as! UITableViewCell
let destinationViewController:ViewController = segue.destinationViewController as! ViewController
// “cell.textLable” is where the value is passed from the SplitView
if let text = cell.textLabel!.text {
let incedent = LocationIncedent(sName: text)
destinationViewController.detailString = "Lat:" + String(stringInterpolationSegment: incedent.incLat) + " Long:" + String(stringInterpolationSegment: incedent.incLong)
DestinationViewController is where the label accepts displays the value. However, how do I get the values sent to a map instead of a label. Moreover, I need to pass latitude and longitude coordinates to a CLLocationCoordinate2D method.
Thanks
You create a property on the destinationViewController for each object you want to pass and assign this property in the segue method.
In your destinationViewController's viewDidLoad() you can user the data signed to these properties to populate a MKMapView...