Switch tab bar failed programmatically in Swift - ios

I want to switch tabbar item 2 but it always goes to item 1. in the log, it says it goes to item 2 as well but it shows item 1. i am using the segue. viewControllers![1]
if segue.identifier == Constants.Identifiers.goToWinTabFromSearchMatch {
let tabBarController = segue.destination as! UITabBarController
let navController = tabBarController.viewControllers![1] as! UINavigationController
let controller = navController.topViewController as! WinViewController
}
#IBAction func doneToBackToWinTab(_ sender: Any) {
print("i wana back to the win tab")
performSegue(withIdentifier: Constants.Identifiers.goToWinTabFromSearchMatch, sender: nil)
}

I too am confused by your code snippet as it merely declares a bunch of constants, you're not performing any action whatsoever here. To change tabs in UITabViewController, simply use the selectedIndex property (it's a simple Int). Set it to a valid value and voilĂ .
However, you have a host of problems in your if block regarding optionals. Force-unwrapping and accessing array fields that may not even exist is just asking for trouble. I highly recommend you do it the right way and unwrap them properly via if let.
i.e., your tabBarController.viewControllers![1] as! UINavigationController is virtually like placing a can of kerosene next to an open fire - you don't know when but it will eventually blow up.

Related

Variable not storing?

I am currently developing a tactical screen app where one can access a database to add to add players into their screens. The database is working fine, I am now trying to pass over player information into the selected position. The player information passes over fine, but I am now having trouble with trying to implement that player information into the selected position:
var selectedP: Int?
#IBAction func selectAPlayer(_ sender: UITapGestureRecognizer) {
self.selectedP = sender.view!.tag
//print (selectedP!)
}
Above is the method which demonstrates how I am trying to save the selected position's tag with selectedP, so I can access its subviews. The correct tag prints out in the above method. However, when I try to call it in another method, the variable returned is always nil. I'm not exactly sure what the problem is. Here is the method where I try to call the selectedP variable:
func setPlayer () {
//print(selectedP!)
}
Simply printing selectedP crashes the program as it is obviously equivalent to nil. Is there anything I am doing wrong?
I must note that the setPlayer() method is called by a segue from another class which is essentially a View Player class. This is shown as a popover in the application. I'm not sure that if you call a popoverController the variables essentially get restored?
Correct me if I'm wrong, but I believe you set your variable as self.selectedP, not selectedP. I'm not familiar with swift, but this concept is fairly universal. In python:
class foo():
def setBar():
self.bar = True
print(str(self.bar)) #prints True
print(str(bar)) #throws error
Figured it out. Had to pass over the variable to the popover, and then back. Here's how I did it in a more generic way:
let viewController = "addPopover"
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: viewController) as? PopoverViewController
// Above we get the popover, below we set a variable in that popover's class.
vc?.varThatNeedsToBeStored = sender.view!.tag
Then in my prepare segue method in the popover class:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let nextScene = segue.destination as? TacticalCentreViewController
nextScene?.varThatNeedsToBeStored = varThatNeedsToBeStored
}
This now returns the correct tag value.

Cannot update embedded child VC from non-parent VC

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!

Nil error assigning Delegate to PopoverPresentationController

I have a problem assigning a Delegate to a PopoverPresentationController using Swift 2.2 in Xcode 7.3
Reason of using a Delegate is I'm trying to trigger a function when pressing outside of a Popover view (class RedeemViewController) to go back to the main menu (class MenuViewController). This happens without a button. The function exists in MenuViewController, but it's not relevant what it does now, so I just included the Delegation part where the error occurs.
class MenuViewController: UIViewController, UIPopoverPresentationControllerDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (sender as! UIButton == btnRedeem) {
let rvc = segue.destinationViewController as! RedeemViewController
let nav = UINavigationController(rootViewController: rvc)
let popover = nav.popoverPresentationController
popover!.delegate = self
}
}
}
Pushing the button btnRedeem, connected to the Segue towards the Popover view, RedeemViewController, leads to
fatal error: unexpectedly found nil while unwrapping an Optional value
In Xcode, the debugger points to
popover!.delegate = self
The Segue exists and is named in the Storyboard. There are no warnings or obsolete references when right-clicking on the view.
Replacing the code with
popover?.delegate = self
leads to a more generic error where the debugger stops in AppDelegate:
libc++abi.dylib: terminating with uncaught exception of type NSException
I looked here but none of the possible causes applies. I have a generic AppDelegate.swift as described here.
Any help is appreciated! I mostly found examples with buttons but do not want to use a button to exit from the Popover since it's not needed.
Additional info on the Delegation: the function I need running on the background is
popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController)
to check when the Popover view (RedeemViewController) is exited and the function I want to trigger within it is one that reveals a new button.
Thank you for reading and maybe you can help me out!
Cheers,
Floris
let nav = UINavigationController(rootViewController: roc)
Here you are creating a brand new navigation controller with the root VC as the segue's destination controller. This isn't going to work - the destination controller is going to be set up with whatever container it needs, you don't create new view controllers in prepareForSegue.
You want to look at the popoverPresentationController of rvc, not of some navigation controller that isn't going to be added to the screen:
if (sender as! UIButton == btnRedeem) {
let rvc = segue.destinationViewController as! RedeemViewController
let popover = rvc.popoverPresentationController
popover!.delegate = self
}

prepareForSegue destination TableViewController

I currently have a button set to go to a TableViewController but decided I wanted to embed that TableViewController in a TabBarController. Well before I did this I had this bit of code in the previous ViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "showListSegue"){
let des = segue.destinationViewController as! TableViewController
des.jsonfile = self.jsonfile
}
}
Now when I try and click the button it give me this error.
Could not cast value of type 'UITabBarController' (0x111c818b0) to
'Forkit.TableViewController' (0x10d1b1490).
I tried the following code with no success as it gives me this error
Value of type "UITabBarController" has no member 'jsonfile'.
let des = segue.destinationViewController as! TableViewController
How can I get around this?
The segue showListSegue now has your TabBarController as it's destinationViewController. This is why it can not cast it to TableViewController, because it's not the one you're looking for. The TabBarController does contain a reference to the ViewController you want though. You likely have two options:
1: Use .viewControllers and find your view in there, like this:
let tabBarController = segue.destinationViewController as! UITabBarController
let des = tabBarController.viewControllers![0]
In which [0] selects which tab's ViewController you need.
2: Select the right tab and use .selectedViewController
let tabBarController = segue.destinationViewController as! UITabBarController
tabBarController.selectedIndex = 0 // choose which tab is selected
let des = tabBarController.selectedViewController!
Please be aware that you are working with optional values here, so either be absolutely sure you can safely unwrap, or build in checks (casting with as? and if let statements, etc.).
More information: UITabBarController Class Reference
Try This
#IBAction func about(sender: AnyObject) {
performSegueWithIdentifier("about", sender: sender)
}

Can't pass data between View Controllers using condition (Swift 2)

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.

Resources