Swift delegates isn't working - ios

I'm trying to use delegates between two controllers but it doesn't work as it should be
protocol saveDelegate: class {
func saveSite()
}
class AuditSiteViewController: UIViewController {
weak var delegate: saveDelegate?
#IBAction func saveButton(sender: UIBarButtonItem) {
print("Saved")
delegate?.saveSite()
}
}
class AuditDetailsViewController: UIViewController, saveDelegate {
var mainView: AuditSiteViewController?
override func viewDidLoad() {
super.viewDidLoad()
mainView?.delegate = self
}
func saveSite() {
print("delegated")
}
}
it should print delegated but it only prints "saved"?

You can use delegate, but have you debug and check that mainView is the correct instance?
My suggestion in this case would be to use NSNotification instead. You can add a observer in your viewDidLoad and post a notification on the saveButton()
class AuditDetailsViewController: UIViewController {
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AuditDetailsViewController.saveSite), name: "SaveSite", object: nil)
}
}
class AuditSiteViewController: UIViewController {
#IBAction func saveButton(sender: UIBarButtonItem) {
NSNotificationCenter.defaultCenter().postNotificationName("SaveSite", object: nil)
}
}

In my opinion there are only two reasons possible:
First:
In the moment of calling mainView?.delegate = self mainView is nil. Then the delegate isn't assigned. Set a breakpoint there and you will see it.
Second:
In the moment of calling delegate?.saveSite() the delegate is nil. That may be because your instance of AuditDetailsViewController was deinit by you or system. System removes the instance if noone holds a strong reference to it anymore. Implement the deinit method and set a breakpoint in it to see when it happens.

Looks like the mainView is nil when you set the delegate. Try to set the reference when you instantiate the detail view controller.
Anyway, maybe what you want is to delegate the saving action from the detailViewController to the AuditSiteViewController and handle in this last VC the savings.

Related

Parent VC delegate methods not called when embedded containerVC methods are called

I have a Parent VC with a Child VC embedded in a container. Both VCs conform to delegate, but only the child delegate methods are called. How can I get both VC's delegate methods to respond? Am I missing something with delegate pattern for container views? Thanks in advance for any help.
Central class:
public protocol BLEManagerDelegate: class {
func bLEManagerShowAlert(message: String)
}
public class BLEManager: NSObject {
static let sharedInstance = BLEManager()
weak var delegate: BLEManagerDelegate?
public func postMessage() {
delegate?.bLEManagerShowAlert(message: message)
}
}
ParentVC
class HomeVC: ContentViewController, BLEManagerDelegate {
var bLEManager = BLEManager.sharedInstance
override func viewWillAppear(_ animated: Bool) {
bLEManager.delegate = self
}
// delegate methods
func bLEManagerShowAlert(message: String) {
// THIS METHOD IS NOT GETTING CALLED
}
}
Container view embedded into ParentVC
class ChildVC: UITableViewController, BLEManagerDelegate {
var bLEManager = BLEManager.sharedInstance
override func viewWillAppear(_ animated: Bool) {
bLEManager.delegate = self
// delegate methods
func bLEManagerShowAlert(message: String) {
// This method IS getting called
}
}
Your delegate property can only hold a reference to one object at a time. As soon as your ChildVC sets itself as the delegate, the parentVC is no longer the delegate.
If you want to notify multiple objects you could look at using NotificationCenter
Why do you need Singleton BLEManager? Where do you call postMessage()? If alerts are displayed in their own view controllers just write default implementation for a default alert message via protocol extension. Then just implement the methods in VCs for custom messages. If you want multiple delegates you should try this: http://www.gregread.com/2016/02/23/multicast-delegates-in-swift/

Prepare for segue not getting called in UITabbarController

I have gone through all the other posts about this topic but they don't seem to help me.
I have a UITabBarController that is launching two tabs. I want to pass data collected in tab1 to the UITabBar ViewController. I am trying to use delegete protocol for this but I am having trouble setting the delegate variable in the sending ViewController. The prepare for segue never gets called. I cannot even cycle through the viewcontrollers of the tabs inside the ViewDidLoad() of the Tabbar controller as they are not created yet and so nil.
I have used delegates before and it seems rather straightforward. Does it matter that I am using it in a Tabbar?
When I run the code the viewDidLoad() in TabBarViewController is called but not the preparefor segue.
The IBAction donePressed in the MeViewController is called but the delegate is not called as its not set.
Here is the code --
protocol DetailsDelegate: class {
func myDetailsGathered( myDetails: MyDetails )
}
/// RECEIVING VIEW CONTROLLER
class TabBarViewController: UITabBarController, DetailsDelegate
{
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
print("prepare for segue called\n");
if let destinationViewController = segue.destination as? MeViewController
{
destinationViewController.delegate = self
}
}
override func viewDidLoad()
{
print("ViewDidLoad Called \n")
}
func myDetailsGathered(myDetails: MyDetails)
{
self.myDetails = myDetails
print("My details gathered \n")
}
}
---------------
/// SENDING VIEW CONTROLLER
class MeViewController: UIViewController
{
weak var delegate: DetailsDelegate?
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
// I have UIButton in the view and this is invoked when its pressed.
#IBAction func donePressed(_ sender: Any)
{
var infoToPass = MyDetails()
print("looks like we are done")
delegate?.myDetailsGathered(infoToPass: myDetails)
}
}
prepareForSegue is called when you perform a segue. Which you don´t do and that´s why it does not get called.
A segue defines a transition between two view controllers in your
app’s storyboard file.
You should use a singleton class to store variables and access them between different controllers. You declare one like this:
class Singleton {
static let sharedInstance = Singleton()
var name = ""
}
Assign to Singleton:
Singleton.sharedInstance.name = "Some name"
To read from it from whatever controller:
let name = Singleton.sharedInstance.name
First of all, why do you want your tabbarController to receive some info/data though?
The prepare for segue never gets called.
prepareForSegue method will be invoked right after the performSegue. So where's your performSegue method? Or are you sure that that kind of segue going to MeViewController is being performed?
One more option you have is to use NotificationCenter.

Why all IBOutlet(s) found nil when viewdidload is called in App Delegate?

My Xcode debugger reports, every other values include alive (bool value) is set, except all the IBOutlets found nil (myLine, etc.). By the way, everything works when I delete all the code in App Delegate, but I need this view to update frequently, so implement it in applicationWillEnterForeground is necessary. And another thing worth pointing out, in the configure view 1 and 2, I set each outlet's value. And I make app delegate call viewdidload method before that, so all the outlets should hooked up with code already, so those outlets shouldn't be nil.
An error message in the debugger -- fatal error: unexpectedly found nil
while unwrapping an Optional value
import UIKit
class ViewController: UIViewController {
var alive : Bool = true
var aliveDefault = NSUserDefaults.standardUserDefaults()
//IBOulet Connections
#IBOutlet weak var myLine: UILabel!
#IBAction func buttontapped(sender: AnyObject) {
alive = false
NSUserDefaults.standardUserDefaults().setObject(alive, forKey: "alive")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadView()
}
func loadView(){
alive = aliveDefault.boolForKey("alive")
if alive = true {
configureView()
}else{
configureView2()
}
}
func configureView(){
myLine.text = "Random text"
}
func configureView2(){
myLine.text = "Random text 2"
}
}
App Delegate
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
ViewController().viewDidLoad()
ViewController().configureView()
}
Since you are creating two new instances of ViewController in applicationWillEnterForeground using the default initialiser rather than from the storyboard, none of the IBOutlets will be set. You need to update the current instance of the view controller that is on screen.
Rather than doing this from the appDelegate it is probably easier to have your view controller subscribe to the UIApplicationWillEnterForegroundNotification NSNotification and handle the refresh locally. This way you don't need to closely couple your app delegate and your view controller and you don't need to worry if the view controller isn't currently on screen:
class ViewController: UIViewController {
...
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.loadView), name: UIApplicationWillEnterForegroundNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Do ViewController.loadViewIfNeeded() before assigning values to IBOutlets

how can i update other controllers' UI in swift?

I have several controllers in my app. When my app call one function in one controller, I want to update other controllers' UI. How can I achieve that?
class FirstViewController: UIViewController {
func updateUI {...}
}
class SecondViewController: UIViewController {
func updateUI {...}
}
class ThirdViewController: UIViewController{
func updateAllUI {...} # I want call FirstViewController().updateUI() and SecondViewController().updateUI() here
}
But FirstViewController() means I create a new FirstViewController which is I don't want, and FirstViewController has already been created. So how can I call all other controllers' updateUI() in updateAllUI()
Please help, Thank you!
It's usually a pretty bad practice to have view controllers communicate directly. I would use a NSNotification to communicate between view controllers. It's convention to have the name of your notification start with a capital letter and end with the word "Notification".
class FirstViewController: UIViewController {
func updateUI {...}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateUI", name:"TimeToUpdateTheUINotificaiton", object: nil)
}
override deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
class SecondViewController: UIViewController {
func updateUI {...}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateUI", name:"TimeToUpdateTheUINotificaiton", object: nil)
}
override deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
class ThirdViewController: UIViewController{
func updateAllUI {
NSNotificationCenter.defaultCenter().postNotificationName("TimeToUpdateTheUINotificaiton", object: nil)
}
}
Eliminate the parentheses. You don't use them when calling a class function.
FirstViewController.updateUI()
That said.. what you're trying to do is very weird to say the least. You shouldn't use class functions to modify properties of instances of a class. If you have both View controllers on screen at the same time, you should be using a parent controller to command both of them to update their UI when you need to.
If they're not both on screen at the same time, you don't really need to update both UIs.
If you want all your view controllers to react[in this case - updating ui] to an action in one of your view controller, you try posting a Notification.. That is how you broadcast a message in iOS
You post notification from the view controller when your desired action is complete.
All other View controllers would subscribe to that notification and react to it by updating their UI when it is posted.
The below post quickly shows an example to post/observe notifications..
https://stackoverflow.com/a/2677015/4236572
http://www.idev101.com/code/Cocoa/Notifications.html might also be helpful

swift delegate beetween two view controller without segue

My delegate protocol never called
My first controller - ViewController
class ViewController: UIViewController,testProtocol {
#IBAction func btInit(sender: AnyObject) {
println("Bt Init")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initViewController: UIViewController = storyBoard.instantiateViewControllerWithIdentifier("viewTarget") as targetViewController
self.presentViewController(initViewController,animated: false, nil)
}
var targetController = targetViewController();
override func viewDidLoad() {
super.viewDidLoad()
self.targetController.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func testDelegate(){
println(" in my view controller delegate ")
}
}
In my second view controller - targetViewController
protocol testProtocol {
func testDelegate() // this function the first controllers
}
class targetViewController: UIViewController {
#IBAction func BtTarget(sender: AnyObject) {
println("bt target pressed")
delegate?.testDelegate()
}
var delegate : testProtocol?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func testDelegate(){
println(" in my target view controller delegate ")
}
}
Why is testDelegate() never called on ViewController? What am I doing wrong? Thanks.
I have read a lot of posts about this, but all of the examples are given with segue transition, and I don't want use a segue.
Typically you set a new view controller's delegate property in prepareForSegue:. You said you're not using a segue, so you'll need to instantiate the second view controller and present it somehow. You can do this by doing something like:
let storyboard = UIStoryboard(name: "AStoryboardName", bundle: nil)
let secondVC = storyboard.instantiateViewControllerWithIdentifier(anIdentifier) as! targetViewController
secondVC.delegate = self
presentViewController(secondVC, animated: true, completion: nil)
You have a testDelegate() method in both view controllers, but you only want it in the first view controller. Then your second view controller can call delegate?.testDelegate() at the appropriate time.
Finally, you typically want to make delegate properties weak, so I would recommend changing var delegate : testProtocol? to weak var delegate: testProtocol?
I would read up on delegation. Here is a relatively simple 5 step process to delegation that may help you:
Delegation in 5 Steps:
object A is the delegate for object B, and object B will send out the messages:
Define a delegate protocol for object B.
Give object B an optional delegate variable. This variable should be weak.
Make object B send messages to its delegate when something interesting happens, such as the user pressing the Cancel or Done buttons, or when it needs a piece of information.
Make object A conform to the delegate protocol. It should put the name of the protocol in its class line and implement the methods from the protocol.
Tell object B that object A is now its delegate (in prepareForSegue(sender)).

Resources