Looking understand why delegate keep alive - ios

I have the following structure
Main ViewController: it is responsible to call the (A) view controller.
(A) ViewController: create an CustomClass instance and has a delegate for this class.
CustomClass: in each period of 1 second, a message is sent to (A) view controller via delegate.
Until here all works fine. Once I returned to Main ViewController the delegate keep alive, in other words, the delegate updates A(ViewController) variable. I checked that viewDidDisappear of (A) ViewController is called.
When I return again from Main ViewController to (A) ViewController, a new variable instance is created. I don't understand this anyway.
Besides this doubt, I would like to understand why the delegate keep alive when I return to main view controller. I am using a UINavigationItem to navigation.
I am a beginner in IOS development.
Thanks advanced!!!
Edit 1:
The (A)ViewController is called from MainViewController by Segue. The segue was added via storyboard.
MainViewController.swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
AViewController.swift
class ScanDevices : UIViewController, CustomClassDelegate {
var myInts : [Int] = []
var customClass : CustomClass!
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad")
if customClass == nil {
customClass = CustomClass()
customClass.customClassDelegate = self
}
}
override func viewWillAppear(animated: Bool) {
print("viewWillAppear")
}
override func viewDidAppear(animated: Bool) {
print("viewDidAppear")
}
override func viewWillDisappear(animated: Bool) {
print("viewWillDisappear")
}
override func viewDidDisappear(animated: Bool) {
print("viewDidDisappear")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func didDiscoverPeripheralInt(peripheral: Int) {
myInts.append(peripheral)
print("Number = \(myInts.count)")
}
}
CustomClass.swift
class CustomClass : NSObject {
var customClassDelegate : CustomClassDelegate?
// MARK: init
override init() {
super.init()
NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "writeInt", userInfo: nil, repeats: true)
}
func writeInt () {
CustomClassDelegate?.didDiscoverPeripheralInt(3)
}
}

var customClassDelegate : CustomClassDelegate?
You are holding a string reference to your delegate.
It needs to be
weak var customClassDelegate : CustomClassDelegate?
Take a look at the following document :
http://krakendev.io/blog/weak-and-unowned-references-in-swift

Your CustomClass holds strong reference to the delegate. You have to mark the property with weak:
weak var customClassDelegate : CustomClassDelegate?

Related

Sending data to another controller - Without going to him

I want to send data to another controller without opening it.
Example
Main controller:
override func viewDidLoad() {
let vc = SecondViewController()
vc.test = "ABCDFER"
}
Second controller:
var test: String
override func viewDidLoad() {
print(test)
}
How to do it?
It works for me this way
class ViewController: UIViewController {
var otherViewController: OtherViewController!
override func viewDidLoad() {
super.viewDidLoad()
otherViewController = OtherViewController()
otherViewController.test = "ABCDFER"
}
#IBAction func press() {
self.show(self.otherViewController, sender: nil)
}
}
class OtherViewController: UIViewController {
var test: String!
override func viewDidLoad() {
super.viewDidLoad()
print(test)
}
}
In your Main controller, as soon as viewDidLoad() finishes your instance of SecondViewController is destroyed / deallocated. If you want to set a value inSecondViewController at that point, so you can "use" it later, you need to keep a reference to that instance:
So, in Main controller:
var secondVC: SecondViewController?
override func viewDidLoad() {
secondVC = SecondViewController()
secondVC.test = "ABCDFER"
}
Now, later - perhaps on a button tap - you want to use that same instance:
#IBAction func buttonTap(_ sender: Any) {
print("test in secondVC:", secondVC?.test)
}
Keep in mind the view life cycle, if the view viewDidLoad() it's only executed when loading the view through a xib or when view related actions are done with the controller, like addSubview().
The value is being passed and will not be deallocated while your main controller is alive.
You can force a lifecycle event to be called, but isn't recommended at all.
class ViewController: UIViewController {
var otherViewController: OtherViewController!
override func viewDidLoad() {
super.viewDidLoad()
otherViewController = OtherViewController()
otherViewController.test = "ABCDFER"
//Do not do this
otherViewController.viewDidLoad()
}
}
class OtherViewController: UIViewController {
var test: String!
override func viewDidLoad() {
super.viewDidLoad()
print(test)
}
}

Delegate not executing after call swift

I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.

UIscrollview delegation

I am trying to pass data between my two view controllers in my UIscrollview. I am trying to use delegation to send data between Viewcontroller1 and Viewcontroller2. The delegate is Viewcontroller, while the delegator is Viewcontroller1 and Viewcontroller2.
In the code posted below, when the switch in Viewcontroller1 is toggled, it makes the switch in Viewcontroller2 put to the "off" state. I keep on getting the
fatal error: unexpectedly found nil while unwrapping an Optional value
error when I run it, but I have no clue what is causing this problem. Any ideas why?
Below is the Viewcontroller that contains the Uiscrollview and the subviews/childviews
import UIKit
class ViewController: UIViewController, testing {
var vc1 = ViewController1(nibName: "ViewController1", bundle: nil)
var vc2 = ViewController2(nibName: "ViewController2", bundle: nil)
#IBOutlet weak var scrollView: UIScrollView!
func test1() {
vc2.switch2.on = false
}
override func viewDidLoad() {
super.viewDidLoad()
self.addChildViewController(vc1)
self.scrollView.addSubview(vc1.view)
vc1.didMoveToParentViewController(self)
var frame1 = vc2.view.frame
frame1.origin.x = self.view.frame.size.width
vc2.view.frame = frame1
self.addChildViewController(vc2)
self.scrollView.addSubview(vc2.view)
vc2.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
here is the Viewcontoller1 code
protocol testing{
func test1()
}
class ViewController1: UIViewController {
var delegate:testing?
#IBOutlet weak var switch1: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
let vc = ViewController()
self.delegate = vc
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func switch1toggled(sender: AnyObject) {
delegate?.test1()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
and here is the Viewcontroller 2 code
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var switch2: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func switch2toggled(sender: AnyObject) {
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
sorry for the long post, I have been stuck for a week on how to change the state of another switch from toggling a switch in another class, and this was the most efficient way that I found
Try This:
ViewController1
class ViewController1: UIViewController {
let defaults = NSUserDefaults.standardUserDefaults()
let switch1Key = "view1Switch"
override func viewWillAppear(animated: Bool) {
view1Switch.on = defaults.boolForKey(switch1Key)
}
#IBOutlet weak var view1Switch: UISwitch!
#IBAction func view1SwitchChanged(sender: UISwitch) {
defaults.setBool(view1Switch.on, forKey: switch1Key)
}
}
ViewController2
class ViewController2: UIViewController {
let defaults = NSUserDefaults.standardUserDefaults()
let switch1Key = "view1Switch"
override func viewWillAppear(animated: Bool) {
view2Switch.on = defaults.boolForKey(switch1Key)
}
#IBOutlet weak var view2Switch: UISwitch!
#IBAction func view2SwitchChanged(sender: UISwitch) {
defaults.setBool(view2Switch.on, forKey: switch1Key)
}
}
This method syncs the state of the two UISwitches using viewWillAppear and NSUserdefaults. The basic thought pattern is that you save the state of the switch to NSUserdefaults so that when either ViewController1 or ViewController2 is instantiated the view1Switch or view2Switch outlet's .on property is set to the saved value.
Caveats:
The first value for the switch when ViewController1 is instantiated (in the first app run) will be off because boolForKey returns false when there is no saved value. This can be hacked by using view1Switch.on = true directly after view1Switch.on = defaults.boolForKey(switch1Key)
This method makes the switches have the same value. In order to make them have different values, you can use a ! operator like so in ViewController2 view2Switch.on = !defaults.boolForKey(switch1Key). This way switch 1 will always be the opposite of switch 2.
I recommend this method over delegation because, while delegation is powerful, its power doesn't seem needed here.
If you have any questions please ask! :D

Swift - Set a UI element from outside

I have this UIViewController
import UIKit
class UIViewController1: UIViewController {
#IBOutlet weak var someTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And I am trying to set someTitle when I instantiate it from another view controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
let stb = UIStoryboard(name: "Main", bundle: nil)
let vc1 = stb.instantiateViewControllerWithIdentifier("someSTBID") as! UIViewController1
vc1.someTitle.text = "My Title" // it fails here!!!!!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The reason it fails at the line above is that I was trying to force unwrapping a nil optional, which is someTitle.
Please show me a way to set someTitle in this situation.
Your second UIViewController hasn't been loaded yet, so the someTitle IBOutlet will be nil. You got two options:
The easy one: you force the load of the second UIViewController, for example: vc1.view is enough and then you set it (I don't recommend this)
The proper one: you let the second UIViewController be responsible for setting its own title at the right time. If you need to pass the "My title", you can simply pass it via a function like configureVc(title: String), or by exposing a variable like var title: String, so on viewDidLoad of the second UIViewController you would someTitle.text = title.
Do you need it to go into the superclass viewDidAppear, because it should work if you put it into the superclass viewDidLoad(). I hope that helps you and future viewers.

Delegate Not Called In SWIFT iOS

I am Created 2 Views, One is and Used Protocol and Delegate. For first view the Delegate function is not called.
My FirstView Controller : Here I am Accessing the Delegate Function.
import UIKit
class NextViewController: UIViewController,DurationSelectDelegate {
//var secondController: DurationDel?
var secondController: DurationDel = DurationDel()
#IBAction func Next(sender : AnyObject)
{
let nextViewController = DurationDel(nibName: "DurationDel", bundle: nil)
self.navigationController.pushViewController(nextViewController, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
secondController.delegate=self
}
func DurationSelected() {
println("SUCCESS")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
My SecondView Controller : Here I Am creating Delegate.
import UIKit
protocol DurationSelectDelegate {
func DurationSelected()
}
class DurationDel: UIViewController {
var delegate: DurationSelectDelegate?
#IBAction func Previous(sender : AnyObject) {
//let game = DurationSelectDelegate()
delegate?.DurationSelected()
self.navigationController.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
To me, it looks like you're pushing a view controller that you haven't actually set the delegate for. If you change your "Next" function, to include the line
nextViewController.delegate = self
You should see that the delegation works. In doing this, you can also probably remove the creation of "secondController", as it looks like that's redundant.
The naming convention you have followed would confuse fellow developers in your team. The instance should have been
let durationDel = DurationDel(nibName: "DurationDel", bundle: nil)
And then as #Eagerod mentioned, the delegate you would set is
durationDel.delegate = self

Resources