How to pass data between two ViewControllers with TabBarController in Swift - ios

I have a tab bar class (that is attached to my tab bar controller), Like so:
class CaptionTabBarController: UITabBarController, UITabBarControllerDelegate {
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
var logView = self.viewControllers![2] as CaptionsController
logView.log.append("test working!")
}
override func awakeFromNib() {
self.delegate = self;
}
}
And my receiving viewcontroller is like this:
class CaptionsController: UIViewController {
#IBOutlet weak var captionSearchBar: UISearchBar!
#IBOutlet weak var captionsTitle: UILabel!
var receiveImage:UIImage!
var receiveCategoryText:String!
var log = [String]()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
println(log)
}
}
This works when I'm explicitly setting logView.log in CaptionTabBarController.
The result I get in my output windows is as expected. Each tabbar item I click adds "test working!" to the array.
My question is:
How would I be able to get a value from another viewcontroller class to CaptionsController using the tabBarController method I am employing?
This view is a part of a "child" of the tabbar itself, so I'm assuming it already has an instance. All examples I've found just show this, but not how to get data from another class.

The UIViewController that wants to pass the data can store it on your AppDelegate class. Then the UITabBarController delegate method can pull it off and set properties on the receiving UIViewController.
Also, assuming your app is based on the Tab Controller, your AppDelegate can find it with window?.rootViewController as UITabBarController.

Related

Calling methods in child view controller in iOS

I have an app written in Swift for iOS 13 where I present a view modally using storyboards. Once the new view is being presented, I want the parent to call a method which is located inside the child view controller (of my custom class which inherits from UIViewController).
To do this, I plan to have a method inside my parent view controller that gets the modal view controller being presented as its child. Once I get this reference, I will call the child's function from my parent view controller.
I realise this is probably a bad design decision, but I haven't found a way to avoid this approach. I have looked all over stackoverflow to find an answer, but I haven't found any yet. Any help would be much appreciated.
You can instantiate the child view controller and set its properties before presenting it. Then the code that changes the child view controller based on the data is put in the viewDidLoad() method.
class ParentViewController: UIViewController {
func goToChildViewController(object: CustomObject) {
guard let childViewController = self.storyboard?.instantiateViewController(withIdentifier: "child") as? ChildViewController else { fatalError("Cannot instantiate child view controller!") }
childViewController.myProperty = true
childViewController.myObject = myObject // Example of how to pass data from a data model
self.present(childViewController, animated: true)
}
}
class ChildViewController: UIViewController {
var myProperty = false
var myObject: CustomObject? = nil
override viewDidLoad() {
if myProperty {
// Conditional code here
}
{
}
Alternatively, you could trigger a segue in code instead of presenting the child view controller directly.
In this case, you would set up the child view controller inside the parent view controller’s overridden prepare(for:sender:) method, where the child view controller can be accessed using segue.destinationViewController.
Through Segue
When segue triggered maybe through a button press or a table view selection prepare(for:) method will be called on your view controller, at this point you can configure your DestinationViewController by setting some properties.
RootViewController.Swift
#IBOutlet weak var textFieldFirstName: UITextField!
#IBOutlet weak var labelFullname: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let firstVC = segue.destination as? FirstViewController else { return }
firstVC.firstname = textFieldFirstName.text
}
After typing the firstname and tap enter button , firstname value is passed to firstViewController and assigned to related UILabel in viewDidLoad() Method.
FirstViewController.Swift
#IBOutlet weak var textFieldLastName: UITextField!
#IBOutlet weak var labelFirstName: UILabel!
var firstname: String?
override func viewDidLoad() {
super.viewDidLoad()
labelFirstName.text = “Firstname: \(firstname ?? “”)”
}
You can achieve same thing through closure and Delegates

Tab Bar Controller hiding tabs depending on the Login - Using Storyboard

My application has two types of login: Vendor or Customer.
Depending on the login, the user will have access to some tabs on the Tab bar controller.
Example:
- Vendor: Can access Tab 1, Tab 2 and Tab 3
- Customer: Can access Tab 1 and Tab 2
Trying to implement this I made a UITabBarController class (but the line customTabBar.items![2].accessibilityElementsHidden does not hide the tab):
import UIKit
class CustomTabBarController: UITabBarController {
#IBOutlet weak var customTabBar: UITabBar!
var viewControllerList: [UIViewController]?
override func viewDidLoad() {
super.viewDidLoad()
if SingletonLogin.shared.isVendor {
customTabBar.items![2].accessibilityElementsHidden // Tries to hide Tab 3
customTabBar.items![1].title = "Items"
} else {
customTabBar.items![1].title = "Favorites"
}
}
This class is linked with a Custom Tab Bar Controller on my Storyboard:
I am able to do it programmatically, but then I can't use the views on the storyboard.
I would like to find a way to do this using the storyboard.
class CustomTabBarController: UITabBarController {
#IBOutlet weak var customTabBar: UITabBar!
var viewControllerList: [UIViewController]? // Not required
override func viewDidLoad() {
super.viewDidLoad()
if SingletonLogin.shared.isVendor {
self.viewControllers = [self.viewControllers[0], self.viewControllers[1], self.viewControllers[2]]
} else {
self.viewControllers = [self.viewControllers[0], self.viewControllers[1]]
}
}

Delegation from ContainerView to parent ViewController

I have the following setup:
StartViewController has a ContainerView that contains ContainerViewController
I try to find a way to hidden an element in StartViewController after a task is performed in ContainerViewController.
For this I try to use delegation method like this:
StartViewController
class StartViewController: UIViewController, showBannerAdDelegate {
#IBOutlet weak var bannerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView.hidden = false
}
func bannerAdHidden(status: Bool) {
bannerView.hidden = status
}
}
ContainerViewController
protocol showBannerAdDelegate: class {
func bannerAdHidden(status: Bool)
}
class ContainerViewController: UIViewController {
weak var delegate: showBannerAdDelegate! = nil
#IBAction func buttonPressed(sender: UIButton) {
delegate.bannerAdHidden(true)
}
}
If I presented the ContainerViewController I could do in prepareForSegue
let destination = segue.destinationViewController as! ContainerViewController
destination.delegate = self
But in this case both View Controller are always present.
What code should I add to the View Controller to make it work?
Thank you,
If one of the view controllers is inside a container view then it is loaded with an embed segue, which fires when the containing view controller is first loaded. The prepareForSegue method still gets called, so you can set up a delegate exactly as you've described. I always thought embed segues were a little odd (it's not really a segue, more like loading a child view controller) but that's how it works.

Init viewcontroller in app delegate, including tableview delegates

I have a view controller class in my app:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var usersTable: UITableView!
var pendingRequest: FBRequest?
var pendingLoginForSlot: Int!
var userID: String?
var swipeGesture: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
usersTable.delegate = self
usersTable.dataSource = self
self.pendingLoginForSlot = -1
self.usersTable.tableFooterView = UIView(frame: CGRectZero)
}
I want this view controller to be initialized and completely functional in my app delegate after didFinishLaunchingWithOptions When I define this view controller in the app delegate like so:
var VC: ViewController = ViewController()
The VC.usersTable is nil. But when I do my function after viewDidLoad everything is working fine.
So how can I fire functions from within app delegate (e.g. logging in users that are defined in the tableview) and thus have the properties available that get defined in viewDidLoad and expect the same results after I load the VC in a regular manner by loading it via a tab barcontroller?
Thanks!

How to properly segue between view controllers

I've run into this problem while trying in objective-c and now Swift. I create a view controller (vc1), embed it with a navigation controller and then finally create a new view controller (vc2). Then, I put a button on vc1, and control drag it to vc2. I run the app and everything is fine and dandy, clicking on the button will push the new view controller onto the stack and pressing the back button will take me back. However, once I proceed to add new buttons and text fields to vc2, the app will crash once I press my original button on vc1, citing the first line of the app delegate file as the breakpoint. I've tried many things, namely trying to code the segue using the self.performSegueWithIdentifier method but it does not work. I am using Xcode 6.
storyboard.swift (file for vc1):
import UIKit
class storyboard: UIViewController {
#IBOutlet var titleLabel : UILabel
#IBOutlet var orLabel : UILabel
#IBAction func weightedAverageButtonPressed(sender : AnyObject) {
self.performSegueWithIdentifier("weightedAverage1", sender: self)
}
#IBAction func whatINeedButtonPressed(sender : AnyObject) {
}
}
weightedAverage.swift (vc2)(I've commented everything out to get rid of the crash, but no luck):
import UIKit
class weightedAverage: UIViewController {
/* #IBOutlet var weightAverageTitleLabel : UILabel
#IBOutlet var percentagetoCalcLabel : UILabel
#IBOutlet var percentageLabel : UILabel
#IBOutlet var percentageInput : UITextField
#IBAction func continueButton(sender : AnyObject)
{
//var percent = percentageInput.text
// percentageLabel.text = percent
}*/
}
appdelegate.swift:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
return true
}
}
Here is what the debug output says (when I've commented out my IBAction method):
2014-06-19 12:29:12.471 gradeCalc[13217:580024] -[_TtC9gradeCalc10storyboard weightedAverageButtonPressed:]: unrecognized selector sent to instance 0x10bb0ad30
(lldb)
It also highlights the line of app delegate beginning in "class AppDelegate...."
When I uncomment the method which contains the performSegue... method ALL the output log says
"(lldb)" and it still highlights the line of app delegate
Have you tried removing the manual IBAction code for the button and just using the segue made on the Storyboard? It may be trying to segue twice simultaneously if you both used the Storyboard and the Class file to do it.
Edit: All the code in my working example for the two view controllers
First View Controller:
class OneViewController: UIViewController {
#IBOutlet var label1: UILabel
#IBOutlet var label2: UILabel
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
Second View Controller:
class TwoViewController: UIViewController {
#IBOutlet var upperlabel: UILabel
#IBOutlet var lowerlabel: UILabel
#IBOutlet var textbox: UITextField
#IBAction func button(sender: AnyObject) {
var percent = textbox.text
lowerlabel.text = percent
}
override func viewDidLoad() {
super.viewDidLoad()
}
Can't add an image due to NDA, but Storyboard is just NavigationController>VC1>VC2, with two labels in each VC, a text box in VC2, and a button in each. Only the second button has an action in its VC class.

Resources