iOS QR code scanner triggering actions multiple times - swift

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.

Related

How to pass data between two screens while both are elements in NavigationController in Swift?

I have a set of storyboards under the UINavigation Controller. One of these screens contains a table view and I need to send the data of the clicked row to the next screen. I am aware that doing so would require me to use segue. But then how do I get the Navigation Bar and the Navigation Button
For better understanding I have uploaded the screen shot of my first screen. Clicking on the detail button is going to send the challenge object to next screen. However, I am looking forward to having the same navigation bar on the top of the next scree. Any idea how can I accomplish such objective?
Currently if I press on details button, I'll get the details of that challenge in the terminal.
#IBAction func CAStartChallenge(_ sender: UIButton) {
let tag = NSNumber(value: sender.tag) as! Int
print("challenge name to be printed : \(challengeTableConent[tag].display()) ")
}
In your storyboard you want to drag a segue between the view controllers, like so:
Storyboard segue dragging
Use either Show or Push (deprecated) options for the segue. Give this segue an appropriate identifier, such as Details.
Then in your button function you can add these:
#IBAction func CAStartChallenge(_ sender: UIButton) {
let tag = NSNumber(value: sender.tag) as! Int
performSegue(withIdentifier: "Details", sender: challengeTableConent[tag])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Details",
let destination = segue.destination as? DestinationViewController,
let model = sender as? ChallengeTableContentType {
// pass through model to destination view controller
destination.model = model
}
}
You can then configure the destination view controller using its specific model when it appears.
First of all
Passing data through screens using segues is simple. Just check this answer
Pass data through segue
It will explain exactly how to do it
Second
Check the bug on your button I think you mean "Details"
Third
Check the type of segue, you might not used push or show detail. Also select the details view and make sure that "Simulated Metrics -> Topbar" is set to inferred
Some prints to help
This will ensure that the navigation bar comes from the previous controller
This is how your segue should look, do not forget to give an identifier to your segue

Is it possible to have 2 times prepare for segue for 2 different view controllers?

I am swift beginner and I am struggling with my first application. Sorry if my question is too basic, but I get an error message in my code here:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let secVC: NavigationViewController = segue.destinationViewController as! NavigationViewController // here is the error
secVC.receiveImeNaSladkarnica = Label1.text!
secVC.KordaA = Shirina.text!
secVC.KordaB = Duljina.text!
secVC.PodImeNaObekt = Label2.text!
I have created 2 different buttons: Navi Me and More Info buttons.
The first one (Navi Me) is connected with another view controller which is called NavigationViewContorller, there I send all of the data I need by using prepare for segue. Everything was working perfect, but now I have created another button which is called (MoreInfo).It is connected with 3rd viewcontroller called MoreInfoViewController. I think I have connected everything fine, but still when I click on the MoreInfo button the app stops working and I get the following error: Thread 1: signal SIGBART in the marked line. I cannot understand why is it breaking when this segue is for another view controller, nt for the MoreInfoViewController. Would you please advice me what to do or provide me with an example how to fix it
I have tried that:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
switch segue.identifier {
//error here-> case "VC1" :
let secVC: NavigationViewController = segue.destinationViewController as! NavigationViewController
secVC.receiveImeNaSladkarnica = Label1.text!
secVC.KordaA = Shirina.text!
secVC.KordaB = Duljina.text!
secVC.PodImeNaObekt = Label2.text!
//error here -> case "VC2" :
let secVC2: MoreInfoViewController = segue.destinationViewController as! MoreInfoViewController
secVC2.receiveImeNaSladkarnica = Label1.text!
secVC2.KordaA = Shirina.text!
secVC2.KordaB = Duljina.text!
secVC2.PodImeNaObekt = Label2.text!
default:
break
It still doesn't work
Each segue is supposed to have an unique string identifier.
For multiple view controllers use a switch statement
switch segue.identifier {
case "vc1" :
// prepare to go to vc1
case "vc2" :
// prepare to go to vc2
default : break
}
Once again my yesterday comment:
Since you are a beginner in Swift please learn first to consider the naming convention that variable names start with a lowercase letter.

Swift segue performed multiple times

A segue in my app is being called multiple times, which is causing the view to continuously "load" until the app stops calling the segue function. It does this about 4 times until it's finished. After placing breakpoints in the code I noticed that the app is bouncing between these two functions:
if success {
self.performSegueWithIdentifier("startGame", sender: "user")
}
That is the action that triggers the segue (the user swipes something). It then goes to the next breakpoint:
if (segue.identifier == "startGame") {
let destinationNavigationController = segue.destinationViewController as! GameViewController
destinationNavigationController.user = self.users[self.currentUser]
}
The app goes back and forth between the two sections about 4 times. When I created the segue in my storyboard I made sure to wire the segue from the view controller itself (not a table/UI view) to the destination view.
What else can I do to fix this?
Thanks!
In storyboard, create a segue by holding control and dragging from the yellow icon of the 1st view controller to the 2nd view controller.
Give it an id, and the kind should be push if they are embedded in a navigation controller.
In your 1st view controller class:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "startGame" {
let viewController = segue.destinationViewController as! GameViewController
// this next view controller should have property that will store passed value
viewController.user = self.users[self.currentUser]
}
}
When you want to move to the next view controller:
performSegueWithIdentifier("startGame", sender: self)

performSegue after completion handler

ANSWER BELOW
Im facing a little issue that you may help me with.
the app Im working on allows you to request for content based on your location.
the first ViewController is somewhat a form that grab your location / a specified location + some other information to target specific answers.
I need to perform a segue to pass the "question" variables to my second ViewController where I load "answers" with a query based on the question details.
What is causing me trouble is that, whenever the question is geolocalized, I can't retrieve the information using prepareForSegue because it doesn't wait for the geoPoint to be made (completed).The second controller display my latitude and longitude as nil.
I see that I can call the "prepareForSegue" method using "perfomSegueWithIdentifier", and retrieve the information in my second view controller but it perform the segue twice... How can I trigger the segue only when Im ready but using the prepareForSegue data parameter I need to preserve?
Is there a way to pass variable from one controller to another using performSegue?
Any help would be awesome.
Also, while I don't think the code is relevant for my question, here is the code I use.
geoPointing method
#IBAction func doPostQuestion(sender: UIButton) {
var thereQ:PFObject = PFObject(className: "tquestion")
if(somewhereLabel.text == "my location"){
println("Location is geolocalized")
PFGeoPoint.geoPointForCurrentLocationInBackground {
(geoPoint: PFGeoPoint!, error: NSError!) -> Void in
if error == nil {
self.geoLati = geoPoint.latitude as Double
self.geoLong = geoPoint.longitude as Double
self.performSegueWithIdentifier("goto_results", sender:self) // call prepareForSegue when ready but implies to have a segue done on click... (performed twiced)
}
}
}
self.navigationController?.popToRootViewControllerAnimated(true)
}
prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "goto_results"){
// Get Label
let theDestination = (segue.destinationViewController as displayAnswersViewController)
theDestination.lat = self.geoLati
theDestination.lng = self.geoLong
}
}
ANSWER SOLUTION:
As suggested, to solve this problem you just need to create your segue from your viewController1 to your viewController2 and not from a button. This way you can trigger prepareForSegue programatically using the "performSegue" method that will call prepareForSegue anyway.
To solve this problem you just need to create your segue from your viewController1 to your viewController2 and not from a button. This way you can trigger prepareForSegue programatically using the "performSegue" method that will call prepareForSegue anyway.

How To Dismiss Popover From Destination View Controller in Swift

I have a main view controller that has been setup in Interface Builder to open a table view controller via popover segue connected to a button. I want to be able to dismiss the popover when an item inside of my popover table view is selected in didSelectRowAtIndexPath. In Objective-c I can just typecast the the segue in the prepareForSegue delegate to a UIStoryboardPopoverSegueand pass along its UIPopoverController to the table view controller. However, in Swift my downcast fails because it sees the segue as type UIStorybaordPopoverPresentationSegue (when stepping through with the debugger) which doesn't appear to be a public API.
Here's my code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "ShowCollectionsSegue" {
if let collController:CollectionsTableViewController! = segue.destinationViewController as? CollectionsTableViewController {
if let popoverSegue = segue as? UIStoryboardPopoverSegue { // <-- This fails
collController.popover = popoverSegue.popoverController
}
}
}
}
How do I coerce the segue to a UIStoryboardPopoverSegue in order to access its popoverController property?
I'm open to solving the problem of dismissing the popover in response to a table view cell tap a different way, but it seems that when using a segue from the storyboard, the only way to dismiss the popover is by holding onto a reference to the popover controller somehow and the only way to do that as far as I can tell is to cast the segue to a popover segue which Swift doesn't want to let me do. Any ideas?
A strange problem, indeed. I noticed in the documentation, that UIStoryboardPopoverSegue does not inherit from any class. That explains why the cast does not work - UIStoryboardSegue is not its superclass. So I just tried to create a new object - it looks weird but works:
let popoverSegue = UIStoryboardPopoverSegue(
identifier: segue.identifier,
source: self,
destination: segue.destinationViewController as UIViewController)
println("Is there a controller? \(popoverSegue.popoverController.description)")
// YES !!
EDIT
But this controller will not dismiss the popover :(
The fix is to specify the segue in Interface Builder as "Deprecated Segues : Popover". Then the code would be as expected
let popoverSegue = segue as UIStoryboardPopoverSegue
if let destination = segue.destinationViewController as? TableViewController {
destination.delegate = self
self.popoverController = popoverSegue.popoverController
}