I have 2 controllers
and have got 1 global variable, the problem is if I go to controller 2 and click on button northAmericaClick, it will navigate back to control 1, but the value of global variable won't change!
this is my code
controller 1
class OurViewController: UIViewController {
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var selectedServer: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
selectedServer.setTitle(selected server, forState: UIControlState.Normal) // selected server this is global variable
}
controller 2
class selectServerController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func northAmericaClick(sender: AnyObject) {
selectedserver = "North America"
self.navigationController?.popViewControllerAnimated(true)
}
From
You need to use a delegate. Here is an example how do use a delegate in Swift.
On your first ViewController, set your delegate when you load the second VC:
For example, if you are using the Storyboard Editor:
var secondViewController = (segue.destinationViewController.visibleViewController as MySecondViewControllerClass)
secondViewController.delegate = self
Write a Protocol and define a func to write you values back
For example, create a file called "Protocol.swift" and write something like that:
protocol writeValueBackDelegate {
func writeValueBack(value: String)
}
Add the function to your FirstViewController
func writeValueBack(value: String) {
// this is my value from my second View Controller
}
And to your ViewControllerClass
class ViewController: UIViewController, writeValueBackDelegate
Go to the Second View Controller, and add the delegate here:
class SecondViewController: ViewController {
// delegate for FirstViewController
var delegate: writeValueBackDelegate?
On your Second View Controller, you can now use this to call the func in the first View Controller an pass data.
delegate?.writeValueBack("That is a value")
You also need to indicate that your first view controller implements the protocol: class ViewController: UIViewController, writeValueBackDelegate {
A part of doing it with delegate you also can create singleton class ViewControllersDataModel class and share the variable using it:
import Foundation
class ViewControllersDataModel {
static let sharedInstance = ViewControllersDataModel()
var selectedserver: String = ""
private init() {
}
}
And call it like this:
ViewControllersDataModel.sharedInstance.selectedserver = "Selected Option";
Ok, I can do this with this code, only check when viewWillDisapear and call the parent of this view controller in the navicationController:
override func viewWillDisappear(animated: Bool) {
if ((self.navigationController!.viewControllers.last?.isKindOfClass(ActivityMyViewController)) == true){
let backView:MyViewController = self.navigationController!.viewControllers.last as! MyDetailViewController
backView // do whatever you want
}
}
I hope this code can help you, good luck
thanks guys for helping ;)
it was very simple
i just use then when it comeback ^^"
override func viewWillAppear(animated: Bool) {
selectedServer.setTitle(selectedserv, forState: UIControlState.Normal)
}
Related
In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.
I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.
I created a UILabel called order1label on my ThirdViewController.
I want text to be displayed on that label based on what is decided in my SecondViewController.
Below is the code for those two view controllers. When I click on the Submit UIButton in the SecondViewController, I expect the orderType to change to Delivery on the ThirdViewController, and I expect that to be reflected in order1label, but it is not. It still says Takeout.
What am I doing incorrectly? I've been searching for answers for hours and there does not appear to be a simple solution to this extremely simple problem.
import UIKit
class SecondViewController: UIViewController{
var orderType = "Takeout"
#IBAction func SubmitOrderClicked(sender: UIButton) {
orderType = "Delivery"
}
}
Here is the code for my ThirdViewController:
import UIKit
class ThirdViewController: UIViewController {
var orderTextController = SecondViewController().orderType
override func viewDidLoad() {
super.viewDidLoad()
order1Label.text = orderTextController
}
override func viewWillAppear(animated: Bool) {
order1Label.text = orderTextController
}
#IBOutlet var order1Label: UILabel!
}
Declare a global variable orderType in SecondViewController like:
import UIKit
var orderType = "Takeout"
class SecondViewController: UIViewController{
#IBAction func SubmitOrderClicked(sender: UIButton) {
orderType = "Delivery"
}
}
Here is the code the ThirdViewController:
import UIKit
class ThirdViewController: UIViewController {
override func viewWillAppear() {
super.viewWillAppear()
order1Label.text = orderType
}
#IBOutlet var order1Label: UILabel!
}
Hope this satisfies your requirements. Happy coding.
I'm supposing that you want to present ThirdViewController when tapping the button on SecondViewController, so you'd need to change the code to:
import UIKit
class SecondViewController: UIViewController{
var orderType = "Takeout"
#IBAction func SubmitOrderClicked(sender: UIButton) {
orderType = "Delivery"
let thirdController = ThirdViewController()
thirdController.order1Label.text = orderType
self.present(thirdController, animated: true, completion: nil)
}
}
When you call present, the view controller you specify will load and will enter to viewDidLoad. You'd also need to remove this
var orderTextController = SecondViewController().orderType
your problem is only because the label in thirdController needs to be informed after the text is changed in secondController.
After changed your text by clicking the button, you need to inform the label in thirdController to also change the text.
There are several ways that you can achieve that, delegate, notification, block and so on.
If you have further question about using any of ways above please tell me.
I present my secondViewController from (attendanceViewController) and in dismiss completion I'm trying to pass parameters and call functions. The AttendanceViewController appears and the function is called. The problem is that all the Objects are nil when dismiss(#IBOutlet weak var tableView: UITableView! , #IBOutlet weak var boxTypeSKU: UIView!....all)
self.presentingViewController!.dismissViewControllerAnimated(true, completion: { _ i
let attView: AttendanceViewController = self.storyboard!.instantiateViewControllerWithIdentifier("AttendanceViewID") as! AttendanceViewController
attView.currAttendance = self.currAttendance
attView.searchProductWithSKU("\(sku)")
})
I solved my problem using Protocols like this tutorial (http://swiftdeveloperblog.com/pass-information-back-to-the-previous-view-controller/) I think it's more elegant and efficient.
There's my updated code:
In second view Controller (BarcodeScannerViewController.swift) I do it:
protocol BarcodeScannerProtocol {
func setSKUScanner(sku: String)
}
class BarcodeScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var delegate:BarcodeScannerProtocol?
func back() {
let sku = (barcode as NSString).substringWithRange(NSMakeRange(6, 8))
delegate?.setSKUScanner(sku)
self.presentingViewController!.dismissViewControllerAnimated(true, completion: { _ in
}
}
In first view controller (AttendanceViewController.swift):
class AttendanceViewController: UIViewController, BarcodeScannerProtocol {
var strSKUScanner : String?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let skuScanned = strSKUScanner {
searchProductWithSKU(skuScanned)
} else {
fetchProducts()
}
}
// MARK: BarcodeScannerProtocol functions
func setSKUScanner(sku: String) {
self.strSKUScanner = sku
}
}
The first thing to be noticed is that a new instance of AttendanceViewController is being instantiated. This means that the properties are not being set on the correct object. There needs to be a reference to the view controller that presented the secondViewController. How that is done is up to you, but I recommend a callback containing the currAttendance variable. This would be a property on the presented view controller. Once the callback is called by the presented view controller, the parent AttendanceViewController can set its own property and dismiss the presented view controller and call the searchProductWithSKU(_:) method.
I want to update the label in my DashboardViewController from my AccountViewController when the back button is pressed in AccountViewController.
I have tried passing back a variable from 2nd view to 1st view and updating the label in viewDidLoad and in viewWillAppear but it never updates the label when the 1st view is back on screen.
I tried creating a function in 1st view to update the label with a string passed into the function and calling that function from 2nd view but it says that the label is nil so it couldn't be updated.
My latest attempt was to create a delegate but that didn't work either.
Here is my delegate attempt.
class DashboardViewController: UIViewController, AccountViewControllerDelegate {
#IBOutlet weak var welcome_lbl: UILabel!
func nameChanged(name: String){
var full_name = "Welcome \(name)"
welcome_lbl.text = "\(full_name)"
}
override func viewDidLoad() {
super.viewDidLoad()
AccountViewController.delegate = self
}
}
And then in my AccountViewController I have this
protocol AccountViewControllerDelegate{
func name_changed(name: String)
}
class AccountViewController: UIViewController, UITextFieldDelegate {
var info_changed = false
static var delegate: AccountViewControllerDelegate!
#IBAction func back_btn(sender: AnyObject) {
if(info_changed){
AccountViewController.delegate.name_changed(name_tf.text!)
}
self.dismissViewControllerAnimated(true, completion: nil)
}
Did I mess up the delegate process somehow ? Or is there an easier way to do this?
First. Your delegate should be a normal property of AccountViewController. There is no need to update your name when user press back. You can change DashboardViewController`s name when user change name in AccountViewController. When user go back to DashboardViewController. It`s already show the changed name.
protocol AccountViewControllerDelegate{
func name_changed(name: String)
}
class AccountViewController: UIViewController, UITextFieldDelegate {
var delegate: AccountViewControllerDelegate?
// when user change name through textfield or other control
func changeName(name: String) {
delegate?.name_changed(name)
}
}
Second. When DashboardViewController show AccountViewController. I think it should be push. Set DashboardViewController instance be AccountViewController instance`s delegate.
class DashboardViewController: UIViewController, AccountViewControllerDelegate {
#IBOutlet weak var welcome_lbl: UILabel!
func nameChanged(name: String){
var full_name = "Welcome \(name)"
welcome_lbl.text = "\(full_name)"
}
// present or push to AccountViewController
func showAccountViewController {
let accountViewController = AccountViewController()
accountViewController.delegate = self
// do push view controller
}
}