Swift objects won't update after delegate called - ios

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.

Related

Problems Updating View with Swift 5

I am having some trouble updating my secondViewController view in Xcode using Swift 5. I want my app to add two numbers together and show the result in the second ViewController. Although it works the first time, if I return to my previous view and change the numbers, the view does not update.
I tried using viewWillAppear, viewWillDisappear, amongst others, including NSNotificationCenter addObserve, but I have had no luck whatsoever.
Do you have any recommendations? Am I missing something?
Please see below for the code and a screenshot of my ViewControllers:
//
// ViewController.swift
//
import UIKit
var result = ""
var resultFinal = Float(result)
let finalResult = resultFinal!
class ViewController: UIViewController {
#IBOutlet weak var firstNumber: UITextField!
#IBOutlet weak var secondNumber: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func getResult()-> Float{
guard let fNumber = firstNumber.text else {
return 0
}
let firstFloat = Float(fNumber)
guard let sNumber = secondNumber.text else {
return 0
}
let secondFloat = Float(sNumber)
let sumNumber: Float = firstFloat! + secondFloat!
return sumNumber
}
#IBAction func submitSum(_ sender: Any) {
resultFinal = getResult()
print(resultFinal!)
}
}
//
// secondViewController.swift
//
import UIKit
class secondViewController: UIViewController {
#IBOutlet weak var test: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.test.text!=""
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
test.text = String(finalResult)
}
}
Screenshot:
Thanks.
Your problem is with the global variables. It seems from your code that you expect these three to reevaluate every time one of them changes:
var result = ""
var resultFinal = Float(result)
let finalResult = resultFinal!
For example, if you set resultFinal = 4, then finalResult will equal 4. However, those variables only evaluate once––the first time. You can simplify your use of these variables significantly. Replace these three with:
var result: Float?
Then, in ViewController:
class ViewController: UIViewController {
#IBOutlet weak var firstNumber: UITextField!
#IBOutlet weak var secondNumber: UITextField!
func getResult() -> Float {
guard let number1 = Float(firstNumber.text ?? "0") ?? 0
guard let number2 = Float(secondNumber.text ?? "0") ?? 0
return number1 + number2
}
#IBAction func submitSum(_ sender: Any) {
result = getResult()
}
}
Note: I simplified getResult and made it treat empty fields as 0.
In SecondViewController:
class SecondViewController: UIViewController {
#IBOutlet weak var test: UITextField!
override func viewWillAppear(_ animated: Bool) {
test.text = String(result ?? 0)
}
}
Note: self.test.text!="" doesn't really do anything, so I removed it.

Refresh Current View Controller Data Request when XIB Button Clicked

I want to make error handling pages to all of my view controller through, so when error fetching data error, it's not only showing dialog, but showing a XIB files that contain error message and button to refresh. Like this:
Here's the XIB Class code:
import UIKit
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
#IBAction func didTapTryAgain(_ sender: Any) {
Dialog.showProgressDialog(info: "")
}
}
Here's my base controller code, who handling all the problem.
import Foundation
class BaseViewController: UIViewController {
var uiView = UIView();
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
self.view.addSubview(viewErrorMessage)
}
}
And here's how I call it in another class which I extend BaseViewController, so it can show the error problem globally, without I redeclared again the class:
func onFailedDeleteCart(errorMessage: String) {
getErrorMessage(message: errorMessage)
}
Right now I can pass the error message.
The problem is, I want the Refresh button refreshing current View Controller when I click it. Maybe calling current View Controller's viewDidLoad when I click it will be the nice logic but I don't know how to implement it in XIB class. Anyone can solve out this? Thank you!
Approach: 1
Step:1 Create closure for callback
typealias RefreshBlock = (()->())?
Step:2 Define closure in your UIView class
Step:3 Call closure if user tap refresh button
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
var refreshBlock:RefreshBlock!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
// Step : 3
#IBAction func didTapTryAgain(_ sender: UIButton) {
refreshBlock!()
}
}
Step:4 Assign value in closure when addSubview called
class BaseViewController: UIViewController {
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
viewErrorMessage.refreshBlock = {()
self.viewDidLoad()
print("Refresh Contents")
}
self.view.addSubview(viewErrorMessage)
}
}
Approach: 2
Pass your current UIViewController Reference into UIView class. Refer below code.
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
var currentVC:UIViewController!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
#IBAction func didTapTryAgain(_ sender: UIButton) {
currentVC.viewDidLoad()
}
}
class BaseViewController: UIViewController {
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
viewErrorMessage.currentVC = self
self.view.addSubview(viewErrorMessage)
}
}

Pass data between childViewControllers Swift

I put a container inside my MainViewController. Then added FirstViewController inside that container
let containerView = UIView()
view.addSubview(containerView)
let controller = FirstViewController()
addChildViewController(controller)
containerView.addSubview(controller.view)
controller.didMove(toParentViewController: self)
There are a UITextField and a UIButton inside FirstViewController. There is a label inside SecondViewController and var passedText = "" to update the UILabel text. I just want to set the UILabel text according to the UITextField text from FirstViewController when I press UIButton and it should leads to SecondViewController as well. This is the UIButton action method from FirstViewController. I'm not using Storyboard
#IBAction func btnPressed(_ sender: Any) {
let secondVC = SecondViewController()
addChildViewController(secondVC)
view.addSubview(secondVC.view)
secondVC.view.frame = view.bounds
secondVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
secondVC.didMove(toParentViewController: self)
secondVC.passedText = inputTextField.text!
}
This is FirstViewController
class FirstViewController: UIViewController {
#IBOutlet weak var inputTextField: UITextField!
let secondVC = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPressed(_ sender: Any) {
addChildViewController(secondVC)
view.addSubview(secondVC.view)
secondVC.view.frame = view.bounds
secondVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
secondVC.didMove(toParentViewController: self)
secondVC.passedText = inputTextField.text!
}
}
This is SecondViewController
class SecondViewController: UIViewController {
var passedText = ""
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = passedText
}
}
Closure
class ParentViewController: UIViewController {
var embeddedViewController: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController = FirstViewController()
embedViewController(firstViewController)
firstViewController.passDataClosure = { [weak self] text in
let secondViewController = SecondViewController()
self?.embedViewController(secondViewController)
secondViewController.passedText = text
}
}
func embedViewController(_ viewController: UIViewController) {
removeEmbedded()
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addChildViewController(viewController)
view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
embeddedViewController = viewController
}
func removeEmbedded() {
embeddedViewController?.view.removeFromSuperview()
embeddedViewController?.didMove(toParentViewController: nil)
embeddedViewController?.removeFromParentViewController()
}
}
-
class FirstViewController: UIViewController {
var passDataClosure: ((String?) -> Void)?
#IBOutlet weak var inputTextField: UITextField!
#IBAction func btnPressed(_ sender: Any) {
passDataClosure?(inputTextField.text)
}
}
-
class SecondViewController: UIViewController {
var passedText: String? {
didSet {
label.text = passedText
}
}
#IBOutlet weak var label: UILabel!
}
Delegate
class ParentViewController: UIViewController, FirstViewControllerDelegate {
var embeddedViewController: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
let firstViewController = FirstViewController()
embedViewController(firstViewController)
firstViewController.delegate = self
}
func embedViewController(_ viewController: UIViewController) {
removeEmbedded()
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addChildViewController(viewController)
view.addSubview(viewController.view)
viewController.didMove(toParentViewController: self)
embeddedViewController = viewController
}
func removeEmbedded() {
embeddedViewController?.view.removeFromSuperview()
embeddedViewController?.didMove(toParentViewController: nil)
embeddedViewController?.removeFromParentViewController()
}
// MARK: FirstViewControllerDelegate
func firstViewController(_ firstViewController: FirstViewController, pass text: String?) {
let secondViewController = SecondViewController()
embedViewController(secondViewController)
secondViewController.passedText = text
}
}
-
class SecondViewController: UIViewController {
var passedText: String? {
didSet {
label.text = passedText
}
}
#IBOutlet weak var label: UILabel!
}
-
protocol FirstViewControllerDelegate: class {
func firstViewController(_ firstViewController: FirstViewController, pass text: String?)
}
class FirstViewController: UIViewController {
weak var delegate: FirstViewControllerDelegate?
#IBOutlet weak var inputTextField: UITextField!
#IBAction func btnPressed(_ sender: Any) {
delegate?.firstViewController(self, pass: inputTextField.text)
}
}
As far as I can see, this should work:
secondVC.label.text = inputTextField.text
You currently have secondVC.label = inputTextField.text which shouldn't even compile.
you can create a singleton class for your scenario. so that you can access all of your important data from anywhere either from your firstViewController or SecondViewController.
class YourDataClass : NSObject{
var textFieldData = String()
var lblSeconddata = String()
struct Static
{
public static var instance: YourDataClass?
}
class var singleton: YourDataClass
{
if Static.instance == nil
{
Static.instance = YourDataClass()
}
return Static.instance!
}
}
now somewhere in your controller
YourDataClass.Singleton.textFieldData = txtFld.Text
YourDataClass.Singleton. lblSeconddata = lblText.Text
in smame way you can get it anywhere

Swift Delegate is not being called

I have completed all the needed code for delegate to work. In my viewcontroller:
class ViewController: UIViewControllerCustomViewDelegate
I also have this:
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
func goToNextScene() {
print("GOTOSCENE2")
}
And in my custom view I have this:
import UIKit
protocol CustomViewDelegate: class { // make this class protocol so you can create `weak` reference
func goToNextScene()
}
#available(iOS 10.0, *)
class ImageHeaderView : UIView {
#IBOutlet weak var followme: UISwitch!
#IBOutlet weak var profileImage : UIImageView!
#IBOutlet weak var backgroundImage : UIImageView!
weak var delegate: CustomViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor(hex: "E0E0E0")
self.profileImage.layer.cornerRadius = self.profileImage.frame.size.height / 2
self.profileImage.clipsToBounds = true
self.profileImage.layer.borderWidth = 1
self.profileImage.layer.borderColor = UIColor.white.cgColor
//self.profileImage.setRandomDownloadImage(80, height: 80)
//self.backgroundImage.setRandomDownloadImage(Int(self.frame.size.width), height: 100)
}
#IBAction func followme(_ sender: AnyObject) {
UserDefaults.standard.set(followme.isOn, forKey: "followme")
}
#IBAction func logout(_ sender: AnyObject) {
delegate?.goToNextScene()
print("GOTOSCENE")
}
}
There is are no errors thrown but when I click/tap the button, nothing happens. It just prints "GOTOSCENE".
What I feel is
Your problem is right here
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
I think you have added imageHeaderview from storyboard and in viewdidload you are creating new object of ImageHeaderView and assigning delegate to newly created object.
Try to outlet your ImageHeaderView and assign delegate to outleted object.
Hope this will fix your issue.

How to access IBoutlet of one view controller in another view controller using swift?

How can I change the label text to textfield text?
This source code change the label text to textfield text which are in the same class, but I want to change the label in different class.
label is in the ViewController.swift and textfield is in the ProfileViewController.swift
This is the source code:
ViewController.swift
import UIKit
import Social
class ViewController: UIViewController, SideBarDelegate {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var hedgeImage: UIImageView!
#IBOutlet weak var hideView: UIView!
var sideBar:SideBar = SideBar()
override func viewDidLoad() {
}
ProfileViewController.swift
class ProfileViewController: UIViewController {
#IBOutlet weak var ouput: UILabel!
#IBOutlet weak var name: UITextField!
#IBAction func nameChange(sender: AnyObject) {
ouput.text = name.text
}
}
One of the many ways to do this: You can add an observer to textField and update a variable that holds the name text which then you can send to the new vc in prepare for segue. Here is a sample
class ViewController: UIViewController {
var nameRecieved:String?
#IBOutlet weak var name: UILabel!{
didSet{
if nameRecieved != nil { name.text = nameRecieved }
}
}
}
class ProfileViewController: UIViewController, UITextFieldDelegate {
var nameHolder:String?
#IBOutlet weak var name: UITextField! { didSet{ name.delegate = self } }
//notification
var nameTextFieldObserver: NSObjectProtocol?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let center = NSNotificationCenter.defaultCenter()
let q = NSOperationQueue.mainQueue()
nameTextFieldObserver = center.addObserverForName(UITextFieldTextDidChangeNotification, object: name, queue: q){
notification in
self.nameHolder = self.name.text
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if nameTextFieldObserver != nil { NSNotificationCenter.defaultCenter().removeObserver(nameTextFieldObserver!) }
}
//Here you can pass the value of nameHolder to the other vc's var nameRecieved in your prepare for segue
}
You need to catch hold on to your ViewController object and pass the values. Something like this:
#IBAction func nameChange() {
ouput.text = name.text
// given both your views have a same parent
// and this view is the first child
let myViewController = parentViewController!.childViewControllers[1]
myViewController.name.text = name.text
myViewController.printLabelText()
}

Resources