Data transfer between tab view controllers - swift - ios

There are 3 child tab view controllers. There are labels as 0 in view controllers. If the number(0) of labels increases in any view controller, I want to increase from the others. How can i do this data transfer.
class tab1Controller: UIViewController {
#IBOutlet weak var countLabel: UILabel!
var count = ""
override func viewDidLoad() {
super.viewDidLoad()
count = countLabel.text!
UserDefaults.standard.set(count, forKey: "count")
UserDefaults.standard.synchronize()
}
class tab2Controller: UIViewController {
#IBOutlet weak var countLabel2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
countLabel2.text = UserDefaults.standard.string(forKey: "count")
}
I did something like this but it didn't work

I think the simplest way in your case is to update the label text in viewWillAppear method.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
countLabel.text = UserDefaults.standard.string(forKey: "count")
}
You also should update the value in UserDefaults every time the number changes.

Do it i viewWillAppear() instead of viewDidLoad().. Because DidLoad() of all controllers called when tabBar construct and show first tab ...
#IBOutlet weak var countLabel2: UILabel!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
countLabel.text = UserDefaults.standard.string(forKey: "count")
}

I assume that your child controllers are created at the same when the tab controller created. In such cases that needed to notify other existing controllers, you must use NotificationCenter.
A notification dispatch mechanism that enables the broadcast of information to registered observers.
extension Notification.Name {
static let didReceiveCountData = Notification.Name("didReceiveCountData")
}
class tab1Controller: UIViewController {
#IBOutlet weak var countLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Listen notifications for name .didReceiveCountData.
// onDidReceiveCountData(_:) will be called when notification received.
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceiveCountData(_:)), name: .didReceiveCountData, object: nil)
}
// This will be called when the count changes.
#objc func onDidReceiveCountData(_ notification:Notification) {
if let newCount = notification.object as? String {
countLabel.text = newCount
}
}
// Call this when you need to change count and notify other tabs.
private func changeCount(_ newCount: String) {
NotificationCenter.default.post(name: .didReceiveCountData, object: newCount)
}
}
class tab2Controller: UIViewController {
#IBOutlet weak var countLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Listen notifications for name .didReceiveCountData.
// onDidReceiveCountData(_:) will be called when notification received.
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceiveCountData(_:)), name: .didReceiveCountData, object: nil)
}
// This will be called when the count changes.
#objc func onDidReceiveCountData(_ notification:Notification) {
if let newCount = notification.object as? String {
countLabel.text = newCount
}
}
// Call this when you need to change count and notify other tabs.
private func changeCount(_ newCount: String) {
NotificationCenter.default.post(name: .didReceiveCountData, object: newCount)
}
}

Related

How can you update a label when the tab/view controller of a tab bar changes? (swift)

I am working on a app with a tab bar with four separate tabs. I for example save a number that can be changed/modified in three of those tabs. A label, that displays that number is included in all four of those tabs. However, when I change the number in one tab, the labels of the other tabs are not updated when I switch tabs.
I tried including this in each viewDidLoad() of the view controllers of the tabs:
self.tabBarController?.delegate = self
and then used:
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
if tabBarIndex == 2{
updateLabel()
}
}
If I do that in all viewControllers and select the tabs, the view is still changed, but the tabBarController fails, so that the function updateLabel() isn't called at all.
If I only include the code in the First View Controller and expand this part:
if tabBarIndex == 2{
updateLabel()
}
to cover all tabs, the respective functions (updateLabel()) of the classes are called but the Label itself is nil.
#IBOutlet weak var HoursLabel: UILabel!
func updateLabel(){
if HoursLabel != nil{
//code
}
}
And the label isn't updated.
Does someone know how to fix this? Thank you so much in advance :)
hey i got some solutions for you hope this is what you want.
i'm updating your tab label value with Notification Center here is some code
// TabbarController Code
import UIKit
class TabbarViewController: UITabBarController, UITabBarControllerDelegate {
//MARK Life View Cycle
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(self.TabbarNoitifuntionCall), name: NSNotification.Name(rawValue: "CallTabBarNotificationsCenter"), object: nil) // this code will call when ever you update your value in view controller
}
//MARK:- Private Functions
#objc func TabbarNoitifuntionCall(_ notification: Notification) {
self.viewControllers![0].title = "First " + String(notification.object as! Int)
self.viewControllers![1].title = "Second " + String(notification.object as! Int)
}
}
// First Tabbar Controller
import UIKit
class FirstVc: UIViewController {
//MARK:- IBOutlet
#IBOutlet weak var update_lbl: UILabel!
//MARK:- Variables
var count = Int()
//MARK:- Life View Cycle
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK:- Private Function
#IBAction func buttonAction(_ sender: UIButton) {
count = count + 1
update_lbl.text = String(count)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "CallTabBarNotificationsCenter"), object: count) // here we will call notifications center so our labal value upadate
}
}
// Second Tabbar Controller
import UIKit
class SecondVc: UIViewController {
//MARK:- IBOutlet
#IBOutlet weak var update_lbll: UILabel!
//MARK:- Variables
var count = Int()
//MARK:- Life View Cycle
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK:- Private Function
#IBAction func button_action(_ sender: Any) {
count = count + 1
update_lbll.text = String(count)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "CallTabBarNotificationsCenter"), object: count) // here we will call notifications center so our labal value upadate
}
}
i hope this is the simple way if anyone add something then please go head
Thank you.

delegate method not getting called with UITabBarController

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.

How to move data backward from third viewcontroller to first viewcontrlloer using notification center?

I have 3 view controller. I have passed object from first to second and second to third viewcontroller and now editing some data in third viewcontroller and trying send updated data object back to first viewcontroller using notification center. But I am not sure how or which method should i use to post notification which can pass data object to first viewcontrller.
For now I tried as following
// third view controller
import UIKit
protocol editContactDelegate: AnyObject {
func updateContact(contact: Contact)
}
class EditViewController: UIViewController {
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var lblPhoneNumber: UILabel!
#IBOutlet weak var lblEmailID: UILabel!
var editContact: Contact?
weak var delegate: editContactDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
lblName.text = editContact?.name
lblPhoneNumber.text = editContact?.phone
lblEmailID.text = editContact?.email
}
#IBAction func editContact(_ sender: AnyObject) {
///??? which method should i use to post object
NotificationCenter.default.post(name: NSNotification.Name("Test"), object: Contact(name: "abc", position: "xyz", email: "updated#gmail.com", phone: "updated number"))
}
}
//////////
// first view controller
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, editContactDelegate {
func updateContact(contact: Contact) {
print(contact.email)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.tableview.delegate = self
self.tableview.dataSource = self
tableview.register(UINib(nibName: "ContactCell", bundle: nil), forCellReuseIdentifier: "ContactCell")
NotificationCenter.default
.addObserver(self,
selector:#selector(NotificationAct(_:)),
name: NSNotification.Name ("Test"), object: nil)
}
#objc func NotificationAct(_ notification: NSNotification) {
var abc = notification. // how to get object from third viewcontroller ???
}
}
You need
let abc = notification.object as! Contact

How to update a progress view that is on another UIViewController - SWIFT?

I have a UITableViewController that is embedded in a UINavigationController. When I click on a row I am attempting to upload a file. To show the progress of this upload I have decided to use custom popup (another UIViewController) - if anyone has a better idea to show the progress of the upload in this context I am open to it.
The only idea I have to transfer continuous data from one UIViewController to another (if that is possible) is by using a Singleton. My code is below, my issue at the moment is I do not know how to update the progress view even though now it has access to the progress data via the singleton.
class SharedSingleton{
private init(){}
static let shared = SharedSingleton()
var prog: Float = 0
}
UITableViewController:
Using Alamofire to get the fraction of upload completed:
/**TRACK PROGRESS OF UPLOAD**/
upload.uploadProgress { progress in
//print(progress.fractionCompleted)
let mySingleton = SharedSingleton.shared
mySingleton.prog = Float(progress.fractionCompleted)
print("mySingleton.prog: \(mySingleton.prog)")
UIViewController containing popup:
#IBOutlet weak var popUpContainer: UIView!
#IBOutlet weak var uploadStatus: UILabel!
#IBOutlet weak var progressBar: UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
// Make popup have rounded corners
popUpContainer.layer.cornerRadius = 5
popUpContainer.layer.masksToBounds = true
// Call Singleton and assign progress of upload to progress view
// HOW TO UPDATE ??????
let mySingleton = SharedSingleton.shared
progressBar.progress = mySingleton.prog
}
One option would be to use NotificationCenter and in your pop-up view controller subscribe to notifications and in your API callback for the progress, publish a notification.
For example:
UploadNotifications.swift:
import Foundation
extension Notification.Name {
static let UploadProgress = Notification.Name("UploadProgress")
}
UploadProgressViewController.swift (your pop-up):
import UIKit
class UploadProgressViewController: UIViewController {
#IBOutlet weak var progressBar: UIProgressView!
private let progress: Progress = {
let progress = Progress()
progress.completedUnitCount = 0
progress.totalUnitCount = 100
return progress
}()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.uploadDidProgress(_:)), name: .UploadProgress, object: nil)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
#objc private func uploadDidProgress(_ notification: Notification) {
if let progress = notification.object as? Int64 {
self.progress.completedUnitCount = progress
if progress == 100 {
// dismiss/exit
}
}
}
}
Then in your method with the upload progress callback:
upload.uploadProgress { progress in
NotificationCenter.default.post(name: .SyncDidProgress, object: Int64(progress))
}

Swift objects won't update after delegate called

I want a stepper and label to reset to zero after my variable in another class is also reset. The variables reset but the stepper and label do not even after using a delegate.
View Controller:
class ViewController: UIViewController, CircleViewDelegate {
var colors = CircleView()
#IBOutlet weak var circleView1: CircleView!
#IBOutlet weak var redStepper: UIStepper!
#IBOutlet weak var redValue: UILabel!
#IBAction func stepperChange(sender: UIStepper)
{
circleView1.redd1 = Int(redStepper.value);
redValue.text = Int(sender.value).description;
}
func updateRedStepperValue(value: Double) {
redStepper.value = value
redValue.text = Int(colors.redd1.value).description;
}
override func viewDidLoad() {
super.viewDidLoad()
colors.delegate = self
}
}
CircleView:
protocol CircleViewDelegate
{
func updateRedStepperValue(value: Double)
func updateGreenStepperValue(value: Double)
func updateBlueStepperValue(value: Double)
}
class CircleView: UIView
{
var delegate: CircleViewDelegate?
var redd1 = 0
func updateValues()
{
if(redd1==Int(red1))
{
redd1=0;
delegate?.updateRedStepperValue(0.0)//
}
}
}
The problem is that your making a brand new instance of your CircleView.
let cycle = CircleView()
You need to set your delegate to your current working instance.
To do so, you should replace your assignment in your viewDidLoad with the following:
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.sharedApplication().delegate! as! AppDelegate
if let viewControllers = app.window?.rootViewController?.childViewControllers {
viewControllers.forEach { vc in
if let cont = vc as? CircleView {
cont.delegate = self
}
}
}
}
Here's an article with project files.

Resources