How do I pass data from a UIViewController to UITabBarController? - swift

In UIViewController:
class ViewController2: UIViewController {
var points = 0
var pressed = false
#IBOutlet weak var label: UILabel!
#IBAction func slider(_ sender: UISlider) {
number = Int(sender.value)
label.text = String(number)
}
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
}
}
I am trying to do something in a TabBarController if a button in a UIViewController is pressed and also add a number to the number in another TabBarConroller.
Image 1: This shows the connection between my ViewControllers.
Image 2: This shows the first two ViewControllers.)
Image 3: This shows the third and fourth ViewController
Here is my storyboard. I've put a few words to describe what I am trying to do in the images. Please tell me if you need a clearer description. Thank you!

If the ViewController is a child of the UITabBarController that you want to access, you can simply use tabBarController property of the UIViewController, e.g., use this to change selected controller to the first one:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
self.tabBarController?.selectedIndex = 0
}
So let's say that you have a custom UITabBarController subclass, e.g.:
class CustomTabBarController: UITabBarController {
func acceptData(points: Int) {
print(">>> Accepted: \(points)")
// or do anything you need to do with it
}
}
Then you can pass it data as follows:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
if let customTabController = self.tabBarController as? CustomTabBarController {
customTabController.acceptData(points: self.points)
}
}
UPDATE
Since it seems that the current VC is presented by one of the tabBarController child controllers, you will have to access it through the self.presentingViewController:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
if let customTabController = self.presentingViewController?.tabBarController as? CustomTabBarController {
customTabController.acceptData(points: self.points)
}
}
UPDATE 2
Your screenshot are of a very poor quality, your explanation of the problem would require a clarification too, since it is really hard to understand what you try to do. So after the whole discussion in comments I guess this is it:
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
if let tabController = self.presentingViewController?.tabBarController,
let viewController3 = tabController.viewControllers?.filter({ $0 is ViewController3 }).first {
viewController3.acceptData(points: self.points)
}
}

You can pass data as normally
let vc:HomeVC = ApiUtillity.sharedInstance.getCurrentLanguageStoryboard().instantiateViewController(withIdentifier: "HomeVC") as! HomeVC
vc.tempCategoryArray = CategoryArray
self.navigationController?.pushViewController(vc, animated: true)

In your TabBarController class, take a variable say variableToBeSet
class TabBarController: UITabBarController
{
var variableToBeSet: Int = 0 // This is just an example. You can change it as per requirement.
// Rest of class implementation
}
Now in your ViewController :
#IBAction func submitbutton(_ sender: UIButton) {
pressed = true
let tabControllerInstance = self.tabBarController as! TabBarController
tabControllerInstance.variableToBeSet = localVariable // The value which you want to assign
}

Related

Change variable while dismissing modal controller

EDIT: I have decided to change the way my app works, so this problem is solved. Thanks to everyone who helped!
I have a modal controller where when I press a button it dismisses the view. What I want to do is change a variable in another view controller when I dismiss it, is that possible? Or, if this doesn't work, is there a way for me to access the changed variable of another swift file? I will add my code below:
class PopupViewController: UIViewController {
var event = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func dismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func event910(_ sender: Any) {
event = "storyTime"
dismiss(animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.event = event
}
}
I want to pass the changed variable "event" to another view controller, how can I do this?
Delegate View Controller is as follows. : -
it is the place where you will send the data to the next swift file
protocol myprotocol {
func anyfunction(_ param1:String)
}
struct mystruct1 {
var delegate:myprotocol?
// where you want tot start the delegate / send the data to the next file
func anymethod(){
delegate.anyfunction(sendTheDataYouWant)
}
}
// it is here you will receive the data
class anyclass:UIViewController ,myprotocol {
let class1 = mystruct1()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
class1.delegate = self
}
func anyfunction(param1:String){
// here Save the data you want
// because this function will be triggered as delegate will be called
}
}
ps:- I reccomend you to read https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
& apple docs

Can't pass value from FirstVC to SecondVC using segue

I have two ViewControllers connected via Show segue. I need to pass NSSlider's value from ViewController to SecondViewCotroller.
So, moving slider in ViewController a variable updates in SecondViewController.
How to update a value of imagesQty variable?
// FIRST VIEW CONTROLLER
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var slider: NSSlider!
#IBOutlet weak var photosLabel: NSTextField!
#IBAction func segueData(_ sender: NSSlider) {
photosLabel.stringValue = String(slider.intValue) + " photos"
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "SegueIdentifierForSecondVC"), sender: slider)
}
func prepare(for segue: NSStoryboardSegue, sender: NSSlider?) {
if segue.identifier!.rawValue == "SegueIdentifierForSecondVC" {
if let secondViewController =
segue.destinationController as? SecondViewController {
secondViewController.imagesQty = slider.integerValue
}
}
}
}
and
// SECOND VIEW CONTROLLER
import Cocoa
class SecondViewController: NSViewController {
var imagesQty = 30
override func viewWillAppear() {
super.viewWillAppear()
self.view.wantsLayer = true
print("viewWillAppear – Qty:\(imagesQty)")
//let arrayOfViews: [NSImageView] = [view01...view12]
let url = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Desktop/ArrayOfElements")
do {
let fileURLs = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]).reversed()
let photos = fileURLs.filter { $0.pathExtension == "jpg" }
for view in arrayOfViews {
//"imagesQty" is here
let i = Int(arc4random_uniform(UInt32(imagesQty-1)))
let image = NSImage(data: try Data(contentsOf: photos[i]))
view.image = image
view.imageScaling = .scaleNone
}
} catch {
print(error)
}
}
First of all the purpose and benefit of NSStoryboardSegue.Identifier is to create an extension to be able to avoid literals.
extension NSStoryboardSegue.Identifier {
static let secondVC = NSStoryboardSegue.Identifier("SegueIdentifierForSecondVC")
}
Then you can write
self.performSegue(withIdentifier: .secondVC, sender: slider)
and
if segue.identifier! == .secondVC { ...
This error occurs because imagesQty is declared in viewWillAppear rather than on the top level of the class.
Change it to
class SecondViewController: NSViewController {
var imagesQty = 30 // Int is inferred
// override func viewWillAppear() {
// super.viewWillAppear()
// }
}
There is another mistake: The signature of prepare(for segue is wrong. It must be
func prepare(for segue: NSStoryboardSegue, sender: Any?) {
You can‘t change the value because the var is defined in the function and not in the class.
Make your var a class property and it should work.
class SecondViewController: UIViewController {
var imagesQty: Int = 30
...
}

Swift performSegueWithIdentifier shows black screen

I have read through a lot of different posts regarding this issue, but none of the solutions seemed to work for me.
I started a new app and I placed the initial ViewController inside a navigation controller. I created a second view and linked them together on the storyboard with a segue. The segue works successfully, and I can see the data I am transferring in a print statement from the second screen, but the screen shows black.
WelcomeScreen:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueToTraits"{
if let gender = self.selectedGender{
let traitVC = segue.destinationViewController as? TraitViewController
traitVC!.gender = gender
}
}
}
func sendGenderToTraitsView(gender : String?){
performSegueWithIdentifier("segueToTraits", sender: self)
}
#IBAction func button1(sender: UIButton) {
selectedGender = boyGender
self.sendGenderToTraitsView(selectedGender)
}
#IBAction func button2(sender: UIButton) {
selectedGender = girlGender
self.sendGenderToTraitsView(selectedGender)
}
Storyboard:
Link to image of my storyboard
My segue is set as follows:
Link to image of my segue information
Also, my viewControllers are named WelcomeViewController and TraitViewController. They have storyboard id's of welcomeVC and traitsVC.
Any help would be incredibly appreciated. Let me know if you need any other information.
By following the steps that #ronatory had laid out so well for me in the accepted answer, I was able to see that I had set up my TraitViewController as a UIPageViewController instead of UIViewController.. and so it didn't generate any errors, but it just took me to a black screen. Feel silly that I read through my code so many times and never noticed this.
Main point: If you're getting a black screen on a ViewController randomly, make sure your class is extending the correct parent class.
in my case:
class TraitViewController: UIPageViewController {
needed to be
class TraitViewController: UIViewController {
I've build your app and everything works, maybe you've missed something, here is my solution (Note: Code is in Swift 3.0, but should be easy to adopt it to Swift 2.*):
The storyboard:
Set the segueToTraits identifier:
Set the TraitViewController class as custom class in the storyboard:
The view controller with the buttons:
import UIKit
class ViewController: UIViewController {
let boyGender = "boy"
let girlGender = "girl"
var selectedGender: String?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueToTraits"{
if let gender = self.selectedGender {
let traitVC = segue.destination as? TraitViewController
traitVC!.gender = gender
}
}
}
func sendGenderToTraitsView(gender : String?){
performSegue(withIdentifier: "segueToTraits", sender: self)
}
#IBAction func button1(sender: UIButton) {
selectedGender = boyGender
self.sendGenderToTraitsView(gender: selectedGender)
}
#IBAction func button2(sender: UIButton) {
selectedGender = girlGender
self.sendGenderToTraitsView(gender: selectedGender)
}
}
The trait view controller:
import UIKit
class TraitViewController: UIViewController {
var gender: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print("gender: \(gender)")
}
}
Result:
You can find the sample project here

perform segue with the same button to two different viewControllers based on information in uitextfield

I am trying to make my view controller segue to two different view controllers with the same button. But i want the segue's to be done based on information that is in an uitextfield. e.g = if the textfield contains the right information then i will need the segue to perform to segue to viewControllerA if the information does not match my array of strings, ViewController will segue to viewControllerB.
Sample code:
#IBOutlet var pcTextField: UITextField!
#IBOutlet var odButton: UIButton!
var activePcText = ["over", "left", "weak", "never"]
override func viewDidLoad() {
super.viewDidLoad()
pcTextField.delegate = self
let tapRecognizer = UITapGestureRecognizer(target: self, action: "hideKeyboard")
view.addGestureRecognizer(tapRecognizer)
odButton.hidden = true
self.pcTextField.addTarget(self, action: "pcEmpty", forControlEvents: UIControlEvents.EditingChanged)
}
#IBAction func odButton(sender: AnyObject) { }
I tried to implement an if statement as..
#IBAction func odButton(sender: AnyObject) {
if pcTextField.text!.containsString(activePcText) { }
}
I just get an error saying:
Cannot convert value of type '[String]' to expected argument type 'String'
#IBOutlet weak var pcTextField: UITextField!
let activePcText = ["over", "left", "weak", "never"]
#IBAction func buttonPressed(sender: UIButton) {
let text = pcTextField.text?.lowercaseString ?? ""
let destination = activePcText.map { $0.lowercaseString }.contains(text) ? "viewControllerA" : "viewControllerB"
performSegueWithIdentifier(destination, sender: self)
}
This makes it case insensitive.
This is an example of a prepareForSegue method that would accomplish what it sounds like you want to do
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueIdentifier" {
if myTextField.text = "words inside text field" {
let controller = segue.destinationViewController as! MyViewController
} else if myTextField.text = "different words in text field" {
let controller = segue.destinationViewController as! MyOtherViewController
}
}
}
So depending on the text inside of myTextField, the segue will be performed to either MyViewController or MyOtherViewController
And then in the #IBAction of whichever button you want to trigger the segue you would add performSegueWithIdentifier("segueIdentifier")

Increase a counter in the first interface controller by pressing a button in the second interface controller

I would like to increase the value of the property in the first interface controller in the IBAction (Add1) method of the second interface controller
and then use the value of this property to update the label when the first view controller is activated
I was able to increase the value , but the value increases even if I press the back button.
I need to find a solution so that when I press the IBAction in the second interface controller , I can get that result and use it in the first interface controller and update the label.
here is the code:
First interface controller:
Blockquote
#IBOutlet weak var resultButtonLabel: WKInterfaceButton!
#IBAction func resultButton() {
pushControllerWithName("secondInterfaceController", context: self)
}
override func willActivate() {
super.willActivate()
resultButtonLabel.setTitle("\(counter++)")
}
Blockquote
Second interface controller:
Blockquote
var counter = 1
#IBAction func weScored() {
counter++
popController()
}
Blockquote
The easy way to implement this idea is use of NSUserDefaults
In your firstViewController you can read values form NSUserDefaults this way:
override func viewDidLoad() {
super.viewDidLoad()
let score = NSUserDefaults().integerForKey("Score")
resultButtonLabel.text = "\(score)"
}
and into your SecondViewController you can increase this counter with NSUserDefaults this way:
import UIKit
class SecondViewController: UIViewController {
var counter = Int()
override func viewDidLoad() {
super.viewDidLoad()
counter = NSUserDefaults().integerForKey("Score")
}
#IBAction func weScored(sender: AnyObject) {
counter++
NSUserDefaults().setInteger(counter, forKey: "Score")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
}
Hope this will help.