I'm trying to run a function in a container view that I added in the storyboard editor but when I call it in the parent view controller, nothing happens. I want to be able to call a function and change a property of the child view controller triggered from the parent. Am I doing something wrong or should I just be doing this another way?
TLDR: Trigger a function in a child view controller from parent
Parent View Controller:
// ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var routeConfirmationView: UIView! //This is the container view that I'm trying to work with
var RouteSelectionViewController: RouteSelectionViewController?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(routeConfirmationView)
self?.RouteSelectionViewController?.getRidOfLoadingCover(isHidden: true) //The code that isn't doing anything
}
}
Container View Controller:
import UIKit
class RouteSelectionViewController: UIViewController {
#IBOutlet weak var loadingCoverView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad(
}
//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}
}
Storyboard:
To call functions, or access properties, in a View Controller embedded in a Container View, you need to get and keep a reference to that controller.
When the Container View loads the embedded VC, it calls Prepare For Segue. Grab your reference there:
class WithContainerViewController: UIViewController {
var routeSelectionVC: RouteSelectionViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? RouteSelectionViewController {
// save reference to VC embedded in Container View
self.routeSelectionVC = vc
}
}
#IBAction func didTap(_ sender: Any) {
if let vc = routeSelectionVC {
vc.getRidOfLoadingCover(isHidden: true)
}
}
}
class RouteSelectionViewController: UIViewController {
#IBOutlet weak var loadingCoverView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
}
//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}
}
You will likely next ask about calling a function in the "parent" VC from the embedded VC. This can be done with protocol / delegate pattern, or with closures. Again, you can set that up in prepare for segue:
class WithContainerViewController: UIViewController {
var routeSelectionVC: RouteSelectionViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? RouteSelectionViewController {
// set the closure in the VC embedded in Container View
vc.callbackClosure = {
self.routeSelectionButtonTapped()
}
// save reference to VC embedded in Container View
self.routeSelectionVC = vc
}
}
#IBAction func didTap(_ sender: Any) {
if let vc = routeSelectionVC {
vc.getRidOfLoadingCover(isHidden: true)
}
}
func routeSelectionButtonTapped() -> Void {
print("Button in RouteSelectionViewController in Container View was tapped!")
}
}
class RouteSelectionViewController: UIViewController {
#IBOutlet weak var loadingCoverView: UIActivityIndicatorView!
var callbackClosure: (() -> ())?
override func viewDidLoad() {
super.viewDidLoad()
}
//The function that I want to trigger from the other view controller:
func getRidOfLoadingCover (isHidden: Bool){
if (isHidden == true) {
loadingCoverView.alpha = 0
}
else if (isHidden == false) {
loadingCoverView.alpha = 100
}
}
#IBAction func didTap(_ sender: Any) {
callbackClosure?()
}
}
I guess you did not set the RouteSelectionViewController variable (btw., variable names should start with a small letter), so it is nil and nothing happens.
If you want you embed a view controller into another, you need to implement the view controller containment requirements (see documentation).
Related
I have a NewCartViewController that's embedded in a UINavigationController
In my ContainerViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToCart" {
self.cartVC = segue.destination as? NewCartViewController
self.cartVC?.delegate = self
print("cartVC Delegate", cartVC?.delegate) // value is NOT nil here
let pendingCart = PendingCart(color: .blue)
self.cartVC?.setupPaintOrder(cart: pendingCart)
}
}
In my NewCartViewController
protocol NewCartViewControllerDelegate: class {
func vcDidFinishWithCancel(_ newCartViewController: NewCartViewController)
}
class NewCartViewController: UIViewController {
weak var delegate: NewCartViewControllerDelegate?
....
#objc func dismissVC() { // called from the leftBarButtonItem
print(delegate) // nil
}
I've looked at other answers that are related but I'm not sure what's going on here. Any help would be appreciated. Thanks!
Edit - more detail on how the VC is instantiated:
My BuyButtonVC calls a delegate method back to AddToCartContainerVC:
extension AddToCartContainerViewController: BuyButtonViewControllerDelegate {
func buyButtonVCDidPressButton(_ vc: BuyButtonViewController) {
performSegue(withIdentifier: "goToCart", sender: self)
}
}
Which then fires the prepare(for segue:) function from the first code block above where the we look at the the segue.destination and set the delegate.
I have a print on the didSet on the delegate in my NewCartViewController and I see that the delegate is set.
The user goes to the screen, the didSet is never called again as if it was getting set to nil. But then when I try to call the delegate method in the second code block, the delegate is nil
I have tried with the following code and seems to work fine.
Few times we get this unknown behavior and is frustrating when stuck. Try to clean build and quit Xcode clear derived data and do it again.
class ContainerViewController: UIViewController, NewViewControllerDelegate {
weak var cartVC: NewViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToCart" {
self.cartVC = segue.destination as? NewViewController
self.cartVC?.delegate = self
print("cartVC Delegate", cartVC?.delegate) // value is NOT nil here
// let pendingCart = PendingCart(color: .blue)
// self.cartVC?.setupPaintOrder(cart: pendingCart)
}
}
func vcDidFinishWithCancel(_ newViewController: NewViewController) {
print(newViewController.delegate)
self.navigationController?.popViewController(animated: true)
}
}
protocol NewViewControllerDelegate: class {
func vcDidFinishWithCancel(_ newViewController: NewViewController)
}
class NewViewController: UIViewController {
weak var delegate: NewViewControllerDelegate?
#IBOutlet weak var leftBarButtonItem: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(dismissVC))
}
#objc func dismissVC() { // called from the leftBarButtonItem
print(delegate) // nil
delegate?.vcDidFinishWithCancel(self)
}
}
Output: when leftbarbuttonitem clicked
cartVC Delegate Optional(<NetworkExtensionTest.ViewController: 0x7f84c6d07670>)
Optional(<NetworkExtensionTest.ViewController: 0x7f84c6d07670>)
Optional(<NetworkExtensionTest.ViewController: 0x7f84c6d07670>)
I have a main view controller, a container view and 2 child view controllers and i would like to be able to switch between the children (for example: when the application loads for the first time, i would like that the controller containing the MapView to be loaded and when i press the Search Bar found in the main view, the controller with the table to be loaded).
Here is my storyboard: https://i.stack.imgur.com/rDPMe.png
MainScreen.swift
class MainScreen: UIViewController {
#IBOutlet private weak var searchBar: UISearchBar!
#IBOutlet private weak var ContainerView: UIView!
//private var openSearchBar: Bool?
private var openMapView: Bool = true
private var openPlacesList: Bool = false
private var containerView: ContainerViewController!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let containerView = segue.destination as? ContainerViewController
if containerView == nil{
containerView = segue.destination as? ContainerViewController
}
if openMapView == true{
containerView!.moveToMapView()
}
else if openPlacesList == true{
containerView!.MoveToOpenPlaces()
}
}
}
//search bar delegate functions
extension MainScreen: UISearchBarDelegate{
//detects when text is entered
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
openPlacesList = true
openMapView = false
containerView!.MoveToOpenPlaces()
}
}
ContainerViewController.swift:
class ContainerViewController: UIViewController {
private var childViewController: UIViewController!
private var first: UIViewController?
private var sec: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MainToMap"{
first = segue.destination as! MapViewController
self.addChild(first!)
self.view.addSubview(first!.view)
self.didMove(toParent: self)
}else{
sec = segue.destination as! PlacesListController
}
if(first != nil && sec != nil){
interchange(first!,sec!)
}
}
func interchange(_ oldVc: UIViewController,_ newVc: UIViewController ){
oldVc.willMove(toParent: nil)
self.addChild(newVc)
self.view.addSubview(newVc.view)
self.transition(from: oldVc, to: newVc, duration: 2, options: UIView.AnimationOptions.transitionCrossDissolve, animations: {
newVc.view.alpha = 1
oldVc.view.alpha = 0
}, completion: { (complete) in
oldVc.view.removeFromSuperview()
oldVc.removeFromParent()
newVc.willMove(toParent: self)
})
}
func moveToMapView(){
performSegue(withIdentifier: "MainToMap", sender: nil)
}
func MoveToOpenPlaces(){
performSegue(withIdentifier: "MainToSearches", sender: nil)
}
}
The problem is that when I press the search bar, it calls the method interchange and then it just gives a SIGABRT 1 error. I tried this tutorial: https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1 and many more but so far no luck. I am stucked here and don't know how i can solve this problem.
Stack: https://i.stack.imgur.com/Zqpm1.png
SIGABR 1 Error: https://i.stack.imgur.com/NBgEN.png
You appear to be trying to manually transition between child view controllers, but at the same time using segues (which do their own transitioning for you). Eliminate the segues (other than the initial embed segue, if you're using a storyboard with a "container view"), and just manually instantiate the child view controllers using their storyboard IDs. But don't use segues and then try to replace the child view controllers in prepare(for:sender:).
Also, when you use transition(from:to:duration:options:animations:completion:), you should not add the views the the view hierarchy yourself. That method does that for you (unless you use the showHideTransitionViews option, which tells the method that you're taking this over, something we don't need to do here). Likewise, when you use the transitionCrossDissolve option, you don't need to mess with alphas, either.
Thus, using the code snippet from that article you reference, you can do:
class FirstViewController: UIViewController {
#IBOutlet weak var containerView: UIView! // the view for the storyboard's "container view"
#IBOutlet weak var redButton: UIButton! // a button to transition to the "red" child view controller
#IBOutlet weak var blueButton: UIButton! // a button to transition to the "blue" child view controller
// tapped on "transition to red child view controller" button
#IBAction func didTapRedButton(_ sender: UIButton) {
redButton.isEnabled = false
blueButton.isEnabled = true
let oldVC = children.first!
let newVC = storyboard!.instantiateViewController(withIdentifier: "RedStoryboardID")
cycle(from: oldVC, to: newVC)
}
// tapped on "transition to blue child view controller" button
#IBAction func didTapBlueButton(_ sender: UIButton) {
blueButton.isEnabled = false
redButton.isEnabled = true
let oldVC = children.first!
let newVC = storyboard!.instantiateViewController(withIdentifier: "BlueStoryboardID")
cycle(from: oldVC, to: newVC)
}
func cycle(from oldVC: UIViewController, to newVC: UIViewController) {
// Prepare the two view controllers for the change.
oldVC.willMove(toParent: nil)
addChild(newVC)
// Get the final frame of the new view controller.
newVC.view.frame = containerView.bounds
// Queue up the transition animation.
transition(from: oldVC, to: newVC, duration: 0.25, options: .transitionCrossDissolve, animations: {
// this is intentionally blank; transitionCrossDissolve will do the work for us
}, completion: { finished in
oldVC.removeFromParent()
newVC.didMove(toParent: self)
})
}
func display(_ child: UIViewController) {
addChild(child)
child.view.frame = containerView.bounds
containerView.addSubview(child.view)
child.didMove(toParent: self)
}
func hide(_ child: UIViewController) {
child.willMove(toParent: nil)
child.view.removeFromSuperview()
child.removeFromParent()
}
}
That yields:
I'm in my first week of developing in iOS and have become stuck on an issue with passing data between view controllers. My set up consists of a view with VC having a button in it and also a container view (no associated view controller). The container view has an embedded Segue to a TableView with TableViewController. The table has 6 rows and each row has a stepper that can change the value of a text view on the associated row. What I would like to do is collect the values of all the textviews when I press the button on the main view.
I am trying to use delegate to do this but when I press the button the returned value is always nil. I believe the problem is to do with the fact the VC is not being passed to the table view controller via the prepareForSegue function but I'm not sure why? Could be to do with the load order of the controllers?
import UIKit
class PredictionViewController: UIViewController, PredictionDelegate {
var predictionData: String!
#IBOutlet weak var messageTextBox: UITextView!
#IBOutlet weak var predictionSubmitButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.messageTextBox.isEditable = false
}
override func viewDidAppear(_ animated: Bool) {
}
func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "predictionSegue") {
// pass data to next view
let vc = segue.destination as! PredictionsTableViewController
vc.predictionHomeDelegate = self
}
}
func receiveData(with data: String) {
predictionData = data
print(predictionData)
}
#IBAction func predictionSubmitButtonAction(_ sender: UIButton) {
print(predictionData)
}
}
TableViewController: (stripped to minimum)
import UIKit
protocol PredictionDelegate: class {
func receiveData(with data: String)
}
class PredictionsTableViewController: UITableViewController, PredictionDelegate {
weak var predictionHomeDelegate: PredictionDelegate?
#IBOutlet weak var homeTeamScore1: UITextView!
#IBOutlet weak var homeTeamStepper1: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
}
func getPredictionList() {
//does some stuff
self.passDataBackwards()
}
func receiveData(with data: String) {}
func passDataBackwards() {
let data = "{\"score\":\"1\"}"
predictionHomeDelegate?.receiveData(with: data)
}
#IBAction func homeTeamStepper1Action(_ sender: UIStepper) {
let score = Int(sender.value).description
homeTeamScore1.text = score
self.passDataBackwards()
}
}
Any help gratefully received!
Edit:
After comments...
You have the wrong idea about Protocols and Delegates. They are not needed here.
Instead, in your "home" VC, get a reference to the embedded VC. Then, add a function in your embedded VC that you can call to get its data.
// "home" view controller
class PredictionViewController: UIViewController {
// this will be a reference to the embedded view controller
var embeddedVC: PredictionsTableViewController?
#IBAction func getDataButtonTapped(_ sender: Any) {
if let tableData = embeddedVC?.getMyData() {
print("Result: \(tableData)")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "predictionSegue") {
if let vc = segue.destination as? PredictionsTableViewController {
// get a reference to the embedded VC
self.embeddedVC = vc
}
}
}
}
// embedded view controller
class PredictionsTableViewController: UIViewController {
var numTaps = 0
func getMyData() -> String {
return "\(numTaps)"
}
#IBAction func didTap(_ sender: Any) {
numTaps += 1
}
}
You're close, but a couple mistakes...
Here is a very, very simple example. View controller with container, which has a view controller with a button.
Code:
import UIKit
// your protocol
protocol PredictionDelegate: class {
func receiveData(with data: String)
}
// embedded view controller
// NOTE: this should NOT include PredictionDelegate
class PredictionsTableViewController: UIViewController {
weak var predictionHomeDelegate: PredictionDelegate?
#IBAction func didTap(_ sender: Any) {
// on button tap, "send data back"
predictionHomeDelegate?.receiveData(with: "Test")
}
}
// "home" view controller
// NOTE: this DOES include PredictionDelegate
class PredictionViewController: UIViewController, PredictionDelegate {
func receiveData(with data: String) {
print(data)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "predictionSegue") {
if let vc = segue.destination as? PredictionsTableViewController {
// set self as the delegate of the embedded PredictionsTableViewController
vc.predictionHomeDelegate = self
}
}
}
}
Notes:
Do NOT include func receiveData(with data: String) {} in your embedded view controller
Do NOT assign PredictionDelegate to your embedded view controller
You need to hook your segue to the vc itself instead of the cell and use
self.performSegue(withIdentifier: "predictionSegue", sender: nil)
Since these are two separate screens I am not too sure it makes too much sense to have the button on the first view controller that submits data from the second, can the button not just be on the second. If for whatever reason it can't you could pass the data back to the first view controller on segueing back by adding a public variable to the first view controller and adding another prepare method to the second to pass the data back like you have done when adding the delegate.
I cannot get my delegate protocol to work. I used this stack overflow questions as a guide dispatch event to parent ViewController in swift . I don't know if things have changed in Swift 3 since this post, but my function in my parentViewController is never getting called. Here is my setup.
//PROTOCOL
protocol PDPPropDetailsDelegate {
func buttonPressed(PropDetailsVC: propertyDetailsVC)
}
// CHILD VIEW CONTROLLER
class propertyDetailsVC: UIViewController {
var delegate: PDPPropDetailsDelegate?
#IBAction func emailButton(_ sender: AnyObject) {
self.delegate?.buttonPressed(PropDetailsVC: self)
}
}
The Button is getting called in Child View Controller.
// PARENT VIEW CONTROLLER
class ImageDetailsVC: UIViewController, PDPPropDetailsDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "container"{
container = segue.destination as! ContainerViewController
}
}
#IBAction func segmentControlAct(_ sender: Any) {
switch segmentControllerView.selectedIndex {
case 0: print("case 1")
container!.segueIdentifierReceivedFromParent("first")
case 1: print("case 2")
container!.segueIdentifierReceivedFromParent("second")
PropertyDetailsVC.delegate = self // **WHERE I SET DELEGATE**
setUpPropertyDetailsUI(property: filterImages)
default: print("default")
}
}
func buttonPressed(PropDetailsVC: propertyDetailsVC) {
print("BUTTON PRESSED")
}
}
Button Pressed is never called. I assume it has to do with the delegate not getting set properly. Not exactly sure why that would be the case though. My setUpPropertyDetailsUI(property: filterImages) takes the Outlets from that VC and sets that works just fine. I did a breakpoint and it is called when I segment over to the PropertyDetailsVC. Any advice or suggestions?
import UIKit
open class ContainerViewController: UIViewController {
//Manipulating container views
fileprivate weak var viewController : UIViewController!
//Keeping track of containerViews
fileprivate var containerViewObjects = Dictionary<String,UIViewController>()
/** Specifies which ever container view is on the front */
open var currentViewController : UIViewController{
get {
return self.viewController
}
}
fileprivate var segueIdentifier : String!
/*Identifier For First Container SubView*/
#IBInspectable internal var firstLinkedSubView : String!
override open func viewDidLoad() {
super.viewDidLoad()
}
open override func viewDidAppear(_ animated: Bool) {
if let identifier = firstLinkedSubView{
segueIdentifierReceivedFromParent(identifier)
}
}
override open func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func segueIdentifierReceivedFromParent(_ identifier: String){
self.segueIdentifier = identifier
self.performSegue(withIdentifier: self.segueIdentifier, sender: nil)
}
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueIdentifier{
//Remove Container View
if viewController != nil{
viewController.view.removeFromSuperview()
viewController = nil
}
//Add to dictionary if isn't already there
if ((self.containerViewObjects[self.segueIdentifier] == nil)){
viewController = segue.destination
self.containerViewObjects[self.segueIdentifier] = viewController
}else{
for (key, value) in self.containerViewObjects{
if key == self.segueIdentifier{
viewController = value
}
}
}
self.addChildViewController(viewController)
viewController.view.frame = CGRect(x: 0,y: 0, width: self.view.frame.width,height: self.view.frame.height)
self.view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
}
}
}
import UIKit
class EmptySegue: UIStoryboardSegue{
override func perform() {
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
You seem to be a little confused at the flow of the app. Here's an answer I wrote for someone else's question about this same topic:
https://stackoverflow.com/a/45312362/3832646
Your protocol and Child view controller look great, but there are quite a few things amiss with the rest of your code here:
your prepare(for segue:_, sender:_) is typically where you would set the delegate for the destination (child) view controller.
PropertyDetailsVC.delegate = self won't do anything - you need an instance of the view controller to set its delegate.
It looks like you're using some sort of container global variable that I'm not sure what it would be for.
Take a look at the answer I posted and have another go. It's in Swift 3.
I have two view controllers and two views.
In my first view, I set the variable 'currentUser' to false.
I need to be able to set 'currentUser' to true in the second view controller.
When trying to reference 'currentUser' from the second view it's not picking it up as 'currentUser' is defined in the first view controller.
How do I carry across variables with segue?
Set values from Any ViewController to a Second One using segues
Like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "yourIdentifierInStoryboard") {
let yourNextViewController = (segue.destinationViewController as yourNextViewControllerClass)
yourNextViewController.value = yourValue
And in your yourNextViewController class.
class yourNextViewControllerClass {
var value:Int! // or whatever
You can call this also programmatically:
self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)
Set values from your DestinationViewController back to your Primary (First) ViewController
1. Implement a protocol, for example create a file called protocol.swift.
protocol changeUserValueDelegate {
func changeUser(toValue:Bool)
}
2. set the delegate on your second View
class yourNextViewControllerClass {
var delegate:changeUserValueDelegate?
3. set the delegate on load (prepareForSegue)
if(segue.identifier == "yourIdentifierInStoryboard") {
var yourNextViewController = (segue.destinationViewController as yourNextViewControllerClass)
yourNextViewController.delegate = self
4. add Function to FirstViewController
func changeUser(toValue:Bool) {
self.currentUserValue = toValue
}
5. call this function from your SecondViewController
delegate?.changeUser(true)
6. Set the delegate in your FirstViewController
class FirstViewController: UIViewController, ChangeUserValueDelegate {
The problem here is that your currentUser variable is of type Bool, which is a value type. So passing it from your first view controller to your second view controller will in fact create a new Bool instance. What you need is to pass a reference from your first view controller to your second view controller (see Value and Reference Types for more details on value and reference with Swift).
Thereby, according to your needs/preferences, you may choose one of the three following examples.
1. The boxing style
Here, we "box" our Bool inside a class and pass a reference of that class instance to the second view controller.
1.1. Create a CurrentUser class:
class CurrentUser {
var someBooleanValue = true {
didSet {
print(someBooleanValue)
}
}
}
1.2. Create a UIViewController subclass for the first view controller:
import UIKit
class ViewController1: UIViewController {
let currentUser = CurrentUser()
override func viewDidLoad() {
super.viewDidLoad()
currentUser.someBooleanValue = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController2 = segue.destinationViewController as? ViewController2 {
viewController2.currentUser = currentUser
}
}
}
1.3. Create a UIViewController subclass for the second view controller:
import UIKit
class ViewController2: UIViewController {
var currentUser: CurrentUser?
// Link this IBAction to a UIButton or a UIBarButtonItem in the Storyboard
#IBAction func toggleBoolean(sender: AnyObject) {
if let currentUser = currentUser {
currentUser.someBooleanValue = !currentUser.someBooleanValue
}
}
}
2. The closure style
Here, we get a weak reference of our first view controller in a closure and pass this closure to the second view controller.
2.1. Create a UIViewController subclass for the first view controller:
import UIKit
class ViewController1: UIViewController {
var currentUser = true {
didSet {
print(currentUser)
}
}
override func viewDidLoad() {
super.viewDidLoad()
currentUser = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController2 = segue.destinationViewController as? ViewController2 {
let closureToPerform = { [weak self] in
if let strongSelf = self {
strongSelf.currentUser = !strongSelf.currentUser
}
}
viewController2.closureToPerform = closureToPerform
}
}
}
2.2. Create a UIViewController subclass for the second view controller:
import UIKit
class ViewController2: UIViewController {
var closureToPerform: (() -> Void)?
// Link this IBAction to a UIButton or a UIBarButtonItem in the Storyboard
#IBAction func toggleBoolean(sender: AnyObject) {
closureToPerform?()
}
}
3. The protocol-delegate style
Here, we make our first view controller conform to some protocol and pass a weak reference of it to the second view controller.
3.1. Create a custom protocol:
protocol MyDelegate: class {
func changeValue()
}
3.2. Create a UIViewController subclass for the first view controller and make it conform to the previous protocol:
import UIKit
class ViewController1: UIViewController, MyDelegate {
var currentUser = true {
didSet {
print(currentUser)
}
}
override func viewDidLoad() {
super.viewDidLoad()
currentUser = false
}
func changeValue() {
currentUser = !currentUser
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController2 = segue.destinationViewController as? ViewController2 {
viewController2.delegate = self
}
}
}
3.3. Create a UIViewController subclass for the second view controller:
import UIKit
class ViewController2: UIViewController {
weak var delegate: MyDelegate?
// Link this IBAction to a UIButton or a UIBarButtonItem in the Storyboard
#IBAction func toggleBoolean(sender: AnyObject) {
delegate?.changeValue()
}
}
Add an attribute currentUserSecondVC in the destination view controller, and use prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Name Of Your Segue" {
var vc = segue.destinationViewController as NameOfTheSecondViewController
vc.currentUserSecondVC = !currentUser //you can do whatever you want with it in the 2nd VC
}
}
The function that should be defined as override is:
open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "Segue Name Defined In Storyboard") {
//set the property of the designated view controller with the value you need
}
}
Since you're using same variable across the two Viewcontrollers, namely currentUser (type Bool).
So its better to make it a global variable in both classes.
When coming to global variable concept in swift.
Everything by default in swift is public, and thus if you declare something like this:
class FirstViewController: UIViewController {
var someVariable: Boll = YES
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
You can access it and set values as long as you have an instance of it:
var MySecondViewController: FirstViewController = FirstViewController(nibName: nil, bundle: nil)
var getThatValue = MySecondViewController.someVariable