Unwrapping the UILabel error while implementing Delegate and Protocols - ios

Here I am getting error when unwrapping the del UIlabel
#IBOutlet weak var del: UILabel! // my label must change through protocol implementation
del.text = name // error here
Unexpectedly found nil while implicitly unwrapping an Optional value
ViewController.swift
class ViewController: UIViewController,DataPass {
#IBOutlet weak var del: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func seconeView(_ sender: UIButton) {
let selview = storyboard?.instantiateViewController(withIdentifier: "SecondViewController")as!SecondViewController
present(selview,animated: true,completion: nil)
}
func pass(name: String) {
del.text = name // error here Unexpectedly found nil while implicitly unwrapping an Optional value
}
}
SecondViewController.swift
protocol DataPass {
func pass(name:String)
}
class SecondViewController: UIViewController {
let delegate:DataPass! = ViewController()
let label = "Delegate"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func endView(_ sender: UIButton) {
if let del = delegate {
del.pass(name: label)
} else {
print("Delegate property is not set")
}
dismiss(animated: true, completion: nil)
}
}

There are few changes you need to do to make your delegate working:
In second controller make these changes:
protocol DataPass {
func pass(name:String)
}
class SecondViewController: UIViewController {
var delegate:DataPass?
let label = "Delegate"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func endView(_ sender: UIButton) {
if let del = delegate {
del.pass(name: label)
} else {
print("Delegate property is not set")
}
dismiss(animated: true, completion: nil)
}
}
and in first controller :
class ViewController: UIViewController,DataPass {
#IBOutlet weak var del: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func seconeView(_ sender: UIButton) {
let selview = storyboard?.instantiateViewController(withIdentifier: "SecondViewController")as!SecondViewController
selview.delegate = self
present(selview,animated: true,completion: nil)
}
func pass(name: String) {
del.text = name
}
}
Explanation:
In this line let delegate:DataPass! = ViewController() you are creating another instance for your delegate variable which doesn't hold any label. You need to assign your ViewController's instance while presenting the next controller to make it working.

Related

Passing data and delegating between few ViewControllers using Coordinator-Pattern

I'm trying to make simple navigation (using Coordinator) between few view controllers in single story board.
Error I faced is that delegate of AuthenticateErrorDelegate returned nil, that's why I can't #jump# to parent - Login Coordinator to handle my logic
Code:
import UIKit
protocol AuthenticateErrorDelegate: class {
func jump()
}
class AuthenticateErrorViewController: UIViewController {
weak var delegate: AuthenticateErrorDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func backToLogin(_ sender: Any) {
self.delegate?.jump(). //delegate return NIL %(
print("jump to AUTH VC")
}
}
andenter code here Coordinator:
import UIKit
protocol LoginCoordinatorDelegate: class {
func didFinishLogin()
func didErrorLogin()
func openAuthForm()
}
class LoginCoordinator: NSObject {
var navigationController: UINavigationController
var childCoordinators: [NSObject] = []
private var authenticateViewController: AuthenticateViewController
private var authenticateErrorViewController: AuthenticateErrorViewController
weak var delegate: LoginCoordinatorDelegate?
deinit {
print("Deallocating \(self.description)")
}
init(navigationController: UINavigationController) {
self.navigationController = navigationController
let storyboard = UIStoryboard(name: "Login", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "AuthenticateViewController") as! AuthenticateViewController
self.authenticateViewController = vc
let evc = storyboard.instantiateViewController(withIdentifier: "AuthenticateErrorViewController") as! AuthenticateErrorViewController
self.authenticateErrorViewController = evc
}
func start() {
authenticateViewController.name = "AUTH VC"
authenticateViewController.delegate = self
navigationController.setViewControllers([authenticateViewController], animated: true)
}
func end(){
authenticateViewController.delegate = self
navigationController.setViewControllers([authenticateErrorViewController], animated: true)
}
}
// MARK: Authentication Delegate
extension LoginCoordinator: AuthenticateDelegate, AuthenticateErrorDelegate {
func jump() {
delegate?.openAuthForm()
}
}
// MARK: UIViewController Extension
extension UIViewController {
func add(_ child: UIViewController) {
addChild(child)
view.addSubview(child.view)
child.didMove(toParent: self)
}
func remove() {
guard parent != nil else { return }
willMove(toParent: nil)
removeFromParent()
view.removeFromSuperview()
}
}
Calling of AuthenticateDelegate works fine:
import UIKit
protocol AuthenticateDelegate: class {
func authenticateUser(username: String)
}
class AuthenticateViewController: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var usernameField: UITextField!
var name: String?
weak var delegate: AuthenticateDelegate?
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = name ?? nameLabel.text
}
#IBAction func loginAction(_ sender: Any) {
guard let username = usernameField.text else { return }
delegate?.authenticateUser(username: username)
}
}
What I am doing wrong ?
Thanks in advance!

Button Triggered Issues

I'm getting data back from secondVC to firstVC and showing it in a text field.
My VC's are FirstVC with a text field and secondVC with a button.
The flow of my VC is when a user clicks on the text field (here text field action Editing Did Begin called) in firstVC then the secondVC will open. And then when the button clicks in secondVC then goes back to firstVC with some data and data will be show in same text field.
So the above is all working fine.
Now I want that the second time when again I click on the text field (now text field contains some data) so then again go to secondVC.
The problem is that now the text field contains data. When I click on it, it doesn't work because of button action property Editing Did Begin.
How to handle this?
Below is my code,
First VC
import UIKit
class firstViewController: UIViewController, UITextFieldDelegate, MyProtocol {
var valueSentFromSecondViewController : String?
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func myTextFieldACTIONWhenEditingDidBegin(_ sender: Any) {
myTextField.isUserInteractionEnabled = false
let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
secondVC.delegate = self
self.navigationController?.pushViewController(secondVC, animated: true)
}
func setResultsAfterEvaluation(valueSent: String) {
self.valueSentFromSecondViewController = valueSent
print(valueSentFromSecondViewController!)
myTextField.text = valueSent
//print(valueSent)
}
}
Second VC
import UIKit
protocol MyProtocol {
func setResultsAfterEvaluation(valueSent: String)
}
class secondViewController: UIViewController {
var delegate : MyProtocol?
var sentValue : String?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btn(_ sender: Any) {
sentValue = "Ahtazaz"
delegate?.setResultsAfterEvaluation(valueSent: sentValue!)
self.navigationController?.popViewController(animated: true)
}
}
I believe this is a more simple way to do what you are trying to achieve, as it doesn't involve protocols and extra functions. If you have any questions, please ask. :)
First View Controller:
import UIKit
//By declaring a variable outside of any class, it is always active in memory and accessible anywhere.
var textFieldText: String = ""
class ViewController1: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField1: UITextField!
func textFieldDidBeginEditing(_ textField: UITextField) {
//textField.resignFirstResponder is used to dismiss the keyboard. By putting it in this function, it hides the keyboard, which prevents users from entering custom text into your text field.
textField.resignFirstResponder()
let VC2 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
self.navigationController?.pushViewController(VC2, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
//This links textField1 on your storyboard to the textFieldDidBeginEditing function.
textField1.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//Every time the view is about to appear, this is called. This is where we update the text field's text.
textField1.text = textFieldText
}
}
Second View Controller:
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
//If the data you are trying to pass is the button's title, use these two functions.
#IBAction func button1Tapped(_ sender: UIButton) {
textFieldText = (button1.currentTitle)!
self.navigationController?.popViewController(animated: true)
}
#IBAction func button2Tapped(_ sender: UIButton) {
textFieldText = (button2.currentTitle)!
self.navigationController?.popViewController(animated: true)
}
//If the data you are trying to pass is not the button's title, use these.
#IBAction func button1Tapped(_ sender: UIButton) {
textFieldText = "Your Text Here"
self.navigationController?.popViewController(animated: true)
}
#IBAction func button2Tapped(_ sender: UIButton) {
textFieldText = "Your Text Here"
self.navigationController?.popViewController(animated: true)
}
}

Cannot access data passed through delegate

I am trying to understand delegate and I don't get why it says that I have used unresolved identifier 'data'.
//This is my sendingVC
import UIKit
protocol TextFieldDelegate {
func userEnteredText(text: String)
}
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate: TextFieldDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sendButton(_ sender: AnyObject) {
if delegate != nil {
if textField.text != nil {
let data = textField.text
delegate?.userEnteredText(text: data!)
}
}
}
}
The problem is with this code below for my receivingVC I am not able to accesss data variable which should be passed by the delegate.
//This is my receivingVC
import UIKit
class SecondViewController: UIViewController, TextFieldDelegate {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func userEnteredText(text: String) {
label.text = data // Use of unresolved identifier 'data'
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "sendSegue" {
let destVC: ViewController = segue.destination as! ViewController
destVC.delegate = self
}
}
}
Update your code:
func userEnteredText(text: String) {
label.text = text
}

Custom protocol delegate method not calling

I am trying to change lable of one view controller from another view controller using custom protocol but its delegate method is not being called
ViewController3 code:
when i click on close button it's delegate method is not being called in my ViewController2.
protocol ViewController3Delegate: class {
func changeLable(_ text: String)
}
class ViewController3: UIViewController {
weak var delegate: ViewController3Delegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func btnCloseAction(_ sender: Any) {
delegate?.changeLable("fillter applied")
self.dismiss(animated: true, completion: nil)
}
}
ViewController2 code:
class ViewController2: UIViewController,ViewController3Delegate {
#IBOutlet weak var lblReport: UILabel!
let VC3 = ViewController3(nibName: "ViewController3", bundle: nil)
override func viewDidLoad() {
super.viewDidLoad()
VC3.delegate = self
}
func changeLable(_ text: String) {
print("delegate called")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Anybody knows where i am wrong, please suggest me some solution
You have not defined a delegate in your ViewController2 which will be used to the delegate in ViewController3 .
See This :
protocol ViewController3Delegate: class {
func changeLable(_ text: String)
}
class ViewController3: UIViewController {
weak var delegate: ViewController3Delegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func btnCloseAction(_ sender: Any) {
if delgate !=nil {
delegate?.changeLable("fillter applied")
self.dismiss(animated: true, completion: nil)
}
}
}
And then in your ViewController2 class :
class ViewController2: UIViewController,ViewController3Delegate {
#IBOutlet weak var lblReport: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func changeLable(_ text: String) {
print("delegate called")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier = "Yoursegueientifier"
{
let vc = segue.destination as! ViewController3
vc.delegate = self
}
}
}
Note : You have to define your segueidentifer name in your storyboard
You should first present ViewController3 from ViewController2 like this:
let VC3 = ViewController3(nibName: "ViewController3", bundle: nil)
VC3.delegate = self
self.presentViewController(VC3, animated: true, completion: nil)
Also, you can delete this line above viewDidLoad
let VC3 = ViewController3(nibName: "ViewController3", bundle: nil)

View Controller delegate returns nil

My code is below. When I press the 'Done' or 'Cancel' buttons, it doesn't work as I want. I did some debugging, and delegate is nil even though I set it. Please help - thanks.
class ViewController: UIViewController,EditViewControllerDelegate {
#IBOutlet weak var label: UILabel!
//页面跳转时
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditView" {
//通过seque的标识获得跳转目标
let controller = segue.destinationViewController as! EditViewController
//设置代理
controller.delegate = self
//将值传递给新页面
controller.oldInfo = label.text
}
}
func editInfo(controller:EditViewController, newInfo:String){
label.text = newInfo
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
func editInfoDidCancer(controller:EditViewController){
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
import UIKit
protocol EditViewControllerDelegate {
func editInfo(controller:EditViewController, newInfo:String)
func editInfoDidCancer(controller:EditViewController)
}
class EditViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate:EditViewControllerDelegate?
var oldInfo:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if oldInfo != nil{
textField.text = oldInfo
}
}
#IBAction func done(sender: AnyObject) {
delegate?.editInfo(self, newInfo: textField.text!)
}
#IBAction func cancel(sender: AnyObject) {
delegate?.editInfoDidCancer(self)
}
}
Try this,
class ViewController: UIViewController,EditViewControllerDelegate {
var controller: EditViewController?
#IBOutlet weak var label: UILabel!
//页面跳转时
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditView" {
//通过seque的标识获得跳转目标
controller = segue.destinationViewController as! EditViewController
//设置代理
controller.delegate = self
//将值传递给新页面
controller.oldInfo = label.text
}
}
func editInfo(controller:EditViewController, newInfo:String){
label.text = newInfo
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
func editInfoDidCancer(controller:EditViewController){
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
import UIKit
protocol EditViewControllerDelegate {
func editInfo(controller:EditViewController, newInfo:String)
func editInfoDidCancer(controller:EditViewController)
}
class EditViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate:EditViewControllerDelegate?
var oldInfo:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if oldInfo != nil{
textField.text = oldInfo
}
}
#IBAction func done(sender: AnyObject) {
delegate?.editInfo(self, newInfo: textField.text!)
}
#IBAction func cancel(sender: AnyObject) {
delegate?.editInfoDidCancer(self)
}
}
I’m not able to tell from your code how you are handling the opening of EditViewController from your ViewController.
I’m guessing that your prepareForSegue:sender: is not getting called resulting in the delegate not getting set. To fix this, you will need to add a performSegueWithIdentifier:sender: call at the point where the segue needs to happen as in
self.performSegueWithIdentifier("EditView", sender: self)
You should replace whatever code is performing the opening of EditViewController with that call.
I have an app the uses showViewController:sender: to open up a second view controller from the first one even though there is a Show segue defined in my storyboard. The segue in my storyboard doesn’t get used in that case and prepareForSegue:sender: is never called as a result.
If I replace my
showViewController(myVC, sender: self)
with
performSegueWithIdentifier("mySegue", sender: self)
then prepareForSegue:sender: will get called. If I am setting a delegate, like you are, in prepareForSegue:sender:, the delegate will get set before the segue happens.

Resources