How to execute func present in a ViewController from another? - swift

I am a beginner in Swift, and I do not yet understand all the elements.
I am trying to execute a function present in a ViewController (ProjectTabBarController) from another ViewController (ProjectInfosViewController). I end up with an error when I execute the function from the second.
For the context, it is for a navigation button of 3 UIViewController belonging to a UITabBarViewController, itself embedded in a UINavigationController
Thank you in advance ! (Sorry for my bad English)
import UIKit
//MARK:-TAB CONTROLLER
class ProjectTabBarController: UITabBarController {
#IBOutlet weak var ui_saveButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func saveAction(_ sender: Any) {
// code
disableSaveButton() // ALL IT'S FINE HERE
}
func enableSaveButton() {
ui_saveButton.title = "Save"
ui_saveButton.isEnabled = true
}
func disableSaveButton() {
ui_saveButton.title = "Saved"
ui_saveButton.isEnabled = false
} }
//MARK:-PROJECT INFORMATIONS
class ProjectInfosViewController: UIViewController, UITextFieldDelegate {
let superController = ProjectTabBarController()
override func viewDidLoad() {
super.viewDidLoad()
}
func textFieldDidChangeSelection(_ textField: UITextField) {
superController.enableSaveButton() // BUT HERE, DOESN'T
} }
//MARK:-PROJECT FIXTURES
class ProjectFixturesViewController: UIViewController, UITableViewDataSource {
}
//MARK:-PROJECT CONTACT
class ProjectContactViewController: UIViewController {
}

This
let superController = ProjectTabBarController()
is a new vc that's not presented , if the vc is inside the tabController then do
let res = self.tabBarController as! ProjectTabBarController
res.......// call what you need

Related

How can I pass a textfield value from one ViewController to a second ViewController (MacOS Swift)?

I'm working on a project and my ViewController file is starting to get very long. Hence, I want to define all my functions in a second ViewController so I can delete some code from my first ViewController. This works, except for when I try to refer to a value from a textField defined in my FirstViewController, it returns nil. I am new to MacOS development so I would greatly appreciate simple/specific feedback.
This is an example of my first ViewController (it initializes variables and uses functions):
class FirstViewController: NSViewController, NSTextFieldDelegate {
#IBOutlet weak var firstName: NSTextField!
let lastName = "Smith"
#IBAction func buttonPressed(_ sender: Any) {
SecondViewController().printUserName()
}
}
This is my second ViewController (it defines functions):
class SecondViewController: NSViewController, NSTextFieldDelegate {
func printUserName() {
print(FirstViewController().firstName!) // this returns nil :(
print(FirstViewController().lastName) // this returns "Smith" :)
}
}
If you use the UINavigationController to move to the second VC, you can simply define a second view and then access the variables in that view.
example, FirstViewController
class FirstViewController: NSViewController, NSTextFieldDelegate {
#IBOutlet weak var firstName: NSTextField!
let lastName = "Smith"
#IBAction func buttonPressed(_ sender: Any) {
guard let secondView = self.storyboard?.instantiateViewController(identifier: "SecondViewController") as? SecondViewController else {
return
}
self.navigationController?.pushViewController(secondView, animated: false)
secondView.textValue = firstName.text
}
}
SecondViewController
class SecondViewController: NSViewController, NSTextFieldDelegate {
var textValue: String?
override func viewDidLoad() {
printUserName()
}
func printUserName() {
if let text = textValue {
print(text)
}
}
}

Passing an integer value through view controllers with an addition and subtraction counter [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 2 years ago.
I am working on a Magic the Gathering Counter app. For my app, I want to click twentyLife(), thirtyLife(), or fourtyLife() to change the value of a variable in another view controller (i.e. FifthViewController.)
Second view controller code:
import UIKit
class SecondViewController: UIViewController {
var vcFive = FifthViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func twentyLife() {
vcFive.lifePoints.text = String(20)
}
#IBAction func thirtyLife() {
vcFive.lifePoints.text = String(20)
}
#IBAction func fortyLife() {
vcFive.lifePoints.text = String(20)
}
I try and call out the fifthViewController as a variable to change my life points text but it does not work.
Here is my code for the fifthViewController:
import UIKit
class FifthViewController: UIViewController {
#IBOutlet weak var lifePoints: UILabel!
var counter = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func lifeUp() {
counter += 1
lifePoints.text = String(counter)
}
#IBAction func lifeDown() {
counter -= 1
lifePoints.text = String(counter)
}
Any help would be appreciated. This is my first app I have been trying to work on from scratch
You should create an instance of your FifthViewController on your SecondViewController, and a variable on your FifthViewController in which you can save the data passed. Like this:
SecondViewController
import UIKit
class SecondViewController: UIViewController {
let fifthVC = self.storyboard?.instantiateViewController(withIdentifier: "fifthVC") as? FifthViewController
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func twentyLife() {
fifthVC.lifePointsInt = 20
}
#IBAction func thirtyLife() {
fifthVC.lifePointsInt = 30
}
#IBAction func fortyLife() {
fifthVC.lifePointsInt = 50
}
}
FifthViewController
import UIKit
class FifthViewController: UIViewController {
var lifePointsInt = Int()
#IBOutlet weak var lifePoints: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
lifePoints.text = "\(lifePointsInt)"
}
#IBAction func lifeUp() {
lifePoints.text = "\(lifePointsInt + 1)"
}
#IBAction func lifeDown() {
lifePoints.text = "\(lifePointsInt - 1)"
}
}
Remember to type in your Storyboard ID in your storyboard for your FifthViewController as "fifthVC" If your pass to your FifthViewController after selecting your Life Count in your SecondViewController you could add self.navigationController?.pushViewController(fifthVC!, animated: true) at the end of each function.

delegation (while creating sideMenu, delegate? returns nil)

guys.
I'm asking for help. It seems a very easy task, but I can solve it for the whole day.
I'm trying to create a side menu using container view. When a user presses More button(barButtonItem), the whole view slide to the right and menu table appears. I know how to make it using Notifications. But I would like to solve it through delegation. Here is my storyboard.
enter image description here
and code:
import UIKit
class RootViewController: UIViewController, SideMenuDelegate {
#IBOutlet weak var leading: NSLayoutConstraint!
var sideMenuIsOpen = false
var sideMenu: MainViewController?
override func viewDidLoad() {
super.viewDidLoad()
sideMenu?.delegate = self
}
func openSideMenu() {
toggleSideMenu()
}
func toggleSideMenu() {
if sideMenuIsOpen {
leading.constant = 0
} else {
leading.constant = 240
}
}
}
and:
import UIKit
protocol SideMenuDelegate {
func openSideMenu()
}
class MainViewController: UIViewController {
var delegate: SideMenuDelegate?
#IBAction func toggleSideMenu(_ sender: UIBarButtonItem) {
if let delegateUnwrapped = delegate {
delegateUnwrapped.openSideMenu()
} else {
print("nil")
}
}
override func viewDidLoad() {
super.viewDidLoad()
BackgroundImageView.createBackground(insideView: self, image: .mainViewBackground)
}
}
Thank you!
This
var sideMenu: MainViewController? // is nil
override func viewDidLoad() {
super.viewDidLoad()
sideMenu?.delegate = self
}
has no reference to the current displayed main , it's nil when you present the main from the Root give it the reference

Pass data between ViewController and ContainerViewController

I'm working on an app, and need to pass data between view and containerView. I need to send data and receive data from both Views.
Let me explain better:
I can change the Label Master (Touch the Container Button) by protocol, but I can not change the Label Container (Touch the Master button). What happens is the Master connects with the container by a following. But do not have a follow Container linking to the Master.
I tried to add but segue to, but it worked.
The Master View Controller:
import UIKit
protocol MasterToContainer {
func changeLabel(text:String)
}
class Master: UIViewController, ContainerToMaster {
#IBOutlet var containerView: UIView!
var masterToContainer:MasterToContainer?
#IBOutlet var labelMaster: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
let view = segue.destinationViewController as? Container
view!.containerToMaster = self
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func button_Container(sender: AnyObject) {
masterToContainer?.changeLabel("Nice! It's work!")
}
func changeLabel(text: String) {
labelMaster.text = text
}
}
The Container View Controller:
import UIKit
protocol ContainerToMaster {
func changeLabel(text:String)
}
class Container: UIViewController, MasterToContainer {
var containerToMaster:ContainerToMaster?
#IBOutlet var labelContainer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func button_Master(sender: AnyObject) {
containerToMaster?.changeLabel("Amazing! It's work!")
}
func changeLabel(text: String) {
labelContainer.text = text
}
}
Can someone help me?
All you need to do is keep a reference to Container in your master view controller.
That is, you should add an instance variable to Master that will hold a reference to the view controller, not just the view. You'll need to set it in prepareForSegue.
So the beginning of Master View Controller would look something like this:
class Master: UIViewController, ContainerToMaster {
#IBOutlet var containerView: UIView!
var containerViewController: Container?
#IBOutlet var labelMaster: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
containerViewController = segue.destinationViewController as? Container
containerViewController!.containerToMaster = self
}
}
And then in your button function, simply change the label using the variable you just added.
Example:
#IBAction func button_Container(sender: AnyObject) {
containerViewController?.changeLabel("Nice! It's work!")
}
This means you can get rid of your MasterToContainer protocol too.
I tested this code, so I know it works, but unfortunately I am an Objective-C dev, and know nothing about best practices in Swift. So I don't know if this is the best way to go about it, but it certainly works.
Edit:
Here's the exact code I've tested:
Master.swift:
import UIKit
class Master: UIViewController, ContainerToMaster {
#IBOutlet var containerView: UIView!
#IBOutlet var labelMaster: UILabel!
var containerViewController: Container?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
containerViewController = segue.destinationViewController as? Container
containerViewController!.containerToMaster = self
}
}
#IBAction func button_Container(sender: AnyObject) {
containerViewController?.changeLabel("Nice! It's work!")
}
func changeLabel(text: String) {
labelMaster.text = text
}
}
Container.swift:
import UIKit
protocol ContainerToMaster {
func changeLabel(text:String)
}
class Container: UIViewController {
#IBOutlet var labelContainer: UILabel!
var containerToMaster:ContainerToMaster?
#IBAction func button_Master(sender: AnyObject) {
containerToMaster?.changeLabel("Amazing! It's work!")
}
func changeLabel(text: String) {
labelContainer.text = text
}
}
I solved it with this code
To send data from ViewController -> ContainerViewController
Class ViewController : UIViewController {
func sendData(MyStringToSend : String) {
let CVC = childViewControllers.last as! ContainerViewController
CVC.ChangeLabel( MyStringToSend)
}
}
in your ContainerViewController
Class ContainerViewController : UIViewController {
#IBOutlet weak var myLabel: UILabel!
func ChangeLabel(labelToChange : String){
myLabel.text = labelToChange
}
}
To send data from ContainerViewController -> ViewController
Class ContainerViewController : UIViewController {
func sendDataToVc(myString : String) {
let Vc = parentViewController as! ViewController
Vc.dataFromContainer(myString)
}
}
and in ViewController
Class ViewController : UIViewController {
func dataFromContainer(containerData : String){
print(containerData)
}
}
I hope this will help someone.
you can use this extension to access the container child
extension UIViewController {
func getContainerChild<vc:UIViewController>(_ viewController : vc,_ hasNavigation : Bool = true) -> (vc) {
guard let vc = self.children[0] as? UINavigationController else {return viewController}
if hasNavigation {
guard let childVC = vc.children[0] as? PurchasedHistoryListVC else {
return viewController}
return childVC as! vc
} else {
return vc as! vc
}
}
}
so you can do some thing like this in your view Controller
let vc = self.getContainerChild(yourChildViewControllerClass())
vc.functionName()

Setting delegate of another class with screen view to self

I'm fairly new at iOS programming. I have this setup:
ViewController view on IB, with class ViewController
SecondController view on IB, with class secondController
I have protocol:
protocol SecondControllerDelegate {
func getSomething() -> String
}
and I have delegate variable on SecondController:
class secondController: UIViewController {
var delegate: SecondControllerDelegate?
#IBOutlet weak var labelStatus: UILabel!
override func ViewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonTouch(sender: AnyObject) {
labelStatus.text = delegate?.getSomething()
}
func try () {
labelStatus.text = "testing"
}
}
Now, according to the hints everywhere, in order so I can call delegate?.getSomething() at SecondController.buttonTouch(), I need to set like this on viewController:
class ViewController: UIViewController, SecondControllerDelegate {
override func viewDidLoad () {
super.viewDidLoad()
SecondController.delegate = self
}
func doSomething () -> String {
return "testing"
}
}
But this generates error 'SecondController.type' does not have a member named 'delegate'.
Some other websites say:
class ViewController: UIViewController, SecondControllerDelegate {
var secondController = SecondController()
override func viewDidLoad () {
super.viewDidLoad()
secondController.delegate = self
}
func doSomething () -> String {
return "testing"
}
}
With this, there are no error. But if I do something on the second screen that should call the delegate, it doesn't call the delegate, like the SecondController is two different objects (one is created by StoryBoard, one is created manually within the ViewController), i.e. the labelStatus that should have changed to "testing", doesn't change at all. But it changes if function try() is called. How am I supposed to do this?
EDIT: I forgot to mention that I used NavigationController, and segue to transition from first screen to second screen.
Because you try to learn how to build a delegate in Swift, I have written you a plain delegate example below
protocol SecondViewControllerDelegate {
func didReceiveInformationFromSecondViewcontroller (information: String)
}
class ViewController: UIViewController, SecondViewControllerDelegate {
func openSecondViewController () {
if let secondViewControllerInstance: SecondViewController = storyboard?.instantiateViewControllerWithIdentifier("SecondViewController") as? SecondViewController {
secondViewControllerInstance.delegate = self
navigationController?.pushViewController(secondViewControllerInstance, animated: true)
}
}
func didReceiveInformationFromSecondViewcontroller(information: String) {
////Here you get the information, after sendInfoToViewController() has been executed
}
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
func sendInfoToViewController () {
delegate?.didReceiveInformationFromSecondViewcontroller("This ist the information")
}
}
UPDATE
Following the same thing in using Storyboard Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let secondViewControllerInstance: SecondViewController = segue.destinationViewController as? SecondViewController {
secondViewControllerInstance.delegate = self
}
}