Swift Protocol Delegate return nil - ios

protocol testDelegate: class {
func open(channel: String, vc: UIViewController)
}
class test: UIViewController{
weak var delegate: testDelegate?
}
override func viewDidLoad() {
super.viewDidLoad()
if self.delegate != nil {
print("hello")
self.delegate?.openGroupChannel(channel: channel!, vc: self)
}
that is Class Test! protocol init in Test class as well
class calling:testDelegate{
override func viewDidLoad() {
//blah blah
}
func func open(channel: String, vc: UIViewController){
print("calling")
}
This is calling class.
I want to call open func in calling class but it does not calling at all,
even print("hello") in test class is not calling it keeps return nil therefore does not call calling function as well.

You need to set your calling as delegate of test ViewController.
In your calling class create object of test class before navigation and set calling class as delegate of your test class as
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let testVc = segue.destinationViewController as? test {
testVc .delegate = self
}
}
Hope it helps.. Happy Coding!!

Related

Delegate without performSegue not getting called Swift 5

My protocol (where "VehicleRegVCs" is enum):
protocol VehicleRegVCDelegate: class {
func presentedVC(_ currentVC: VehicleRegVCs)
}
My Buyer Class:
class BuyerOwnerInfoViewController: UIViewController {
weak var delegate: VehicleRegVCDelegate?
override func viewDidLoad() {
super.viewDidLoad()
delegate?.presentedVC(.buyerOwnerInfo)
}
}
My Main class (where delegate function "func presentedVC" at the bottom not getting called):
class VehicleRegContainerViewController: ICTViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
func setupView() {
let buyerVC = getBuyerOwnerInfoVC()
buyerVC.delegate = self
}
}
extension VehicleRegContainerViewController: VehicleRegVCDelegate {
func presentedVC(_ currentVC: VehicleRegVCs) {
}
}
I expect "presentedVC" to get called... I made a delegate too...
and in UIViewController extension i have made this function...
func getBuyerOwnerInfoVC() -> BuyerOwnerInfoViewController {
let vc = UIStoryboard.exciseAndTaxation.instantiateViewController(withIdentifier: "BuyerOwnerInfoViewController") as! BuyerOwnerInfoViewController
return vc
}
ViewController inside containerView are connected using segue. You can use prepareForSegue method to get the controller and assign the delegate to it.
NOTE: Go to storyboard and click on the segue connecting the container view and the view controller, and assign it an identifier. In below example i have used "yourSegueIdentifier", replace it with your identifier.
class VehicleRegContainerViewController: ICTViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let buyerVC = segue.destination as? BuyerOwnerInfoViewController {
buyerVC.delegate = self
} else {
print("Error in segue")
}
}
}

How to pass Data from the SearchBar to UILabel?

how do you pass data from search Bar to UILabel in a different view controller
Passing Data between View Controllers
I have tried multiple questions here on stack, but it just doesn't work or the simulator ends up crashing for me
class FirstVC: UIViewController, DataEnteredDelegate {
#IBOutlet weak var label: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondVC
secondViewController.delegate = self
}
}
func userDidEnterInformation(info: String) {
label.text = info
}
}
// protocol used for sending data back
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: String)
}
class SecondVC: UIViewController {
// making this a weak variable so that it won't create a strong reference cycle
weak var delegate: DataEnteredDelegate? = nil
#IBOutlet weak var searchBar: UISearchBar!
#IBAction func sendTextBackButton(sender: AnyObject) {
// call this method on whichever class implements our delegate protocol
delegate?.userDidEnterInformation(info: searchBar.text!)
// go back to the previous view controller
_ = self.navigationController?.popViewController(animated: true)
}
}
I don't find anything suspicious in your code which cause app crash. Meanwhile for data transfer between view controller we can use delegation and NotificationCenter. Below code uses NotificationCenter
let kStringTransfer = "StringTransferNotification"
class FirstViewController: UIViewController, DataEnteredDelegate {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(setString(notify:)), name: NSNotification.Name(rawValue: kStringTransfer), object: nil)
}
func getSecondVC() {
if let second = self.storyboard?.instantiateViewController(withIdentifier: "Second") as? ViewController {
self.navigationController?.pushViewController(second, animated: true)
}
}
#IBAction func searchBarBtn(_ sender: Any) {
//go to search view controller
getSecondVC()
}
#objc func setString(notify: Notification) {
//set search bar string to text field
textField.text = notify.object as? String
}
}
class SecondViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
searchBar.delegate = self
}
#IBAction func goBackBtn(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: kStringTransfer), object: searchBar.text)
self.navigationController?.popViewController(animated: true)
}
}
When I need pass just small values between 2 viewControllers, I don't use a delegate, just create a variable in second view and pass like this.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showView") {
let vc = segue.destination as! secondViewController
vc.stringTemp = "My string"
}
}

iOS Swift, with one common method in protocol and two implementations in two ViewControllers, how to decide which ViewControllers method is invoked?

I have a protocol
protocol HandleEmbedController: class {
func printMsg()
}
and 2 container views and 2 corresponding ViewControllers
class EnemyBaseVC: UIViewController, HandleEmbedController {
override func viewDidLoad() {
super.viewDidLoad()
}
var value1 = ""
func printMsg(){
print("printing some embedded message")
}
}
and
class EnemyBase2VC: UIViewController, HandleEmbedController {
func printMsg() {
print("enemy base 2 message")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
and both use the protocol HandleEmbedController and implement printMsg function.
In the main ViewController I have
class HomeBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var handleEmbedController:HandleEmbedController?
#IBAction func onclick(_ sender: UIButton) {
handleEmbedController?.printMsg()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "embedseg"){
if let embed = segue.destination as? EnemyBaseVC {
self.handleEmbedController = embed
}
}
if (segue.identifier == "embedseg2"){
if let embed = segue.destination as? EnemyBase2VC {
self.handleEmbedController = embed
}
}
}
}
When button is clicked always EnemyBaseVC method is invoked and prints
printing some embedded message
Is there any way to decide which method is to be invoked?
UPDATE
If you have two container views, both segues will be triggered on load, and handleEmbedController will reference the one ViewController that is loaded last.
If you have some logic to decide which one should be referenced, then you can use it to decide which ViewController will be referenced, like so:
class HomeBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var handleEmbedController:HandleEmbedController?
// comes from your decision logic
var decisionMaker: Bool = false
#IBAction func onclick(_ sender: UIButton) {
handleEmbedController?.printMsg()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "embedseg"),
let embed = segue.destination as? EnemyBaseVC,
decisionMaker {
self.handleEmbedController = embed
}
else if (segue.identifier == "embedseg2"),
let embed = segue.destination as? EnemyBase2VC,
!decisionMaker {
self.handleEmbedController = embed
}
}
}
be aware that this will set the handleEmbedController on load, if you need more complex behavior, you might as well handle the assignment of handleEmbedController elsewhere than in the segue.
Since this is a scenario where your base ViewController must communicate with more than one object, you can also use notifications instead of delegations. This way, you can decide which message to send when user taps the button. Your base ViewController would look something like this
class HomeBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// comes from your decision logic
var decisionMaker: Bool = true
#IBAction func onclick(_ sender: UIButton) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "BUTTON_WAS_TAPPED"), object: nil, userInfo: ["decision" : decisionMaker])
}
}
while the enemy ViewControllers would look like this (the second one would be the same except the decision value to handle)
class EnemyBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "BUTTON_WAS_TAPPED"),
object: nil,
queue: nil) { notification in
if let userInfo = notification.userInfo,
let decision = userInfo["decision"] as? Bool,
decision {
self.printMsg()
}
}
}
var value1 = ""
private func printMsg(){
print("printing some embedded message")
}
}

Fatal Error while call method from another class

I have two view controller.
I have one test method in first view controller as follow
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
myLabel.hidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showLabel(){
myLabel.hidden = false
}
}
If i call showLabel from another class it give me fatal error: unexpectedly found nil while unwrapping an Optional value
Another viewController is as follow
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func Save(sender: AnyObject) {
ViewController().showLabel()
}
}
If i call showLabel Method from ViewController the its works fine but if i call it from SecondViewController then i got error.
Both your view controllers use storyboard/xib; when you create such view controller instance in code (as you do in Save function) you need use UINib class or instantiateViewControllerWithIdentifier method of UIStoryboard class, since default initializers (like init() you used for ViewController) know nothing about outlets and storyboard/xib associated files (outlet property myLabel will always be nil).
If your code not just template, then you should refactor it, because performing UI changes in view controller that not presented on screen (and never will be) not have sense
It's because your label "myLabel" is always nil.
Your label is part of your first ViewController. The label loads when the view loads. Since the ViewController view has not loaded, the label has not loaded either, and therefore will be nil
You need use delegate:
1-Creat a protocol with a function describes with you are planning to do.
protocol ViewControllerDelegate: AnyObject {
func showLabel()
}
2-Add a delegate in your SecondViewController with the type if the protocol ViewControllerDelegate.
class SecondViewController: UIViewController {
weak var delegate: ViewControllerDelegate? // add delegate
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Save(sender: AnyObject) {
delegate?.showLabel() // call the method from delegate
}
}
3-Implement the ViewControllerDelegate in your ViewController.
class ViewController: UIViewController, ViewControllerDelegate {
3-Set the delegate in the first view controller inside prepareForSegue:
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
myLabel.hidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showLabel(){
myLabel.hidden = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? SecondViewController {
viewController.delegate = self
}
}
}
just add class before function:
class Math{
class func addition(numberOne: Int, numberTwo: Int) -> Int{
var ans: Int = numberOne + numberTwo
return ans
}
}
Then you can call it from another Swift class like this:
Math.addition(40, numberTwo: 2)
To assign it to a variable i:
let i = Math.addition(40, numberTwo: 2) // -> 42

How can I run delegated method inside IBAction?

I have some methods in second view controller. I access them through delegate - inside func buttonSender. Main goal is to pass currentTitle of a button as an argument to a viewController.addNewMessage. What is the best way to do this?
ChatViewController:
protocol ReactToButtons {
func buttonSender(viewController: MyChatViewController)
}
class MyChatViewController: ChatViewController{
var delegate: ReactToButtons?
override func viewDidLoad() {
super.viewDidLoad()
delegate!.buttonSender(self)
}
}
ContainerViewController:
class ContainerViewController: UIViewController, ReactToButtons {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let myVC = segue.destinationViewController as! MyChatViewController
myVC.delegate = self
}
func buttonSender(viewController: MyChatViewController) {
viewController.addNewMessage(/*HERE GOES CURRENT TITLE*/)
}
#IBAction func leftButtonPressed(sender: AnyObject) {
let currentTitle = sender.currentTitle!
}
I have change my answer to suit your code
class ContainerViewController: UIViewController, ReactToButtons {
var delegate: MyChatViewController!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let myVC = segue.destinationViewController as! MyChatViewController
myVC.delegate = self
}
func buttonSender(viewController: MyChatViewController) {
delegate = viewController
}
#IBAction func leftButtonPressed(sender: AnyObject) {
let currentTitle = sender.currentTitle!
delegate!.addNewMessage(currentTitle)
}
}

Resources