Class properties not setting in swift - ios

I am storing values in SimpleClass from the ViewController and i am getting in Secondview class. But i am unable to get the values from the SimpleClass in SecondClass. Help me
Thanks
Demo Project

The reason it doesn't work is because in SecondClass you are creating a new instance of SimpleClass.
What you really want to do is to pass the SimpleClass instance from ViewController to SecondClass.
One way to do this is using the prepareForSegue method in 'ViewController`
on ViewController.swift:
import UIKit
class ViewController: UIViewController {
var simObject:SimpleClass?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "First Class";
simObject = SimpleClass()
simObject!.userName = "damu";
simObject!.userPassword = "123";
simObject!.address = "344r";
print(simObject!.address)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// pass the simObject to secondClass
if let secondClass = segue.destinationViewController as? SecondClass {
secondClass.simpleClass = simObject
}
}
}
On SeconClass.swift
import UIKit
class SecondClass: UIViewController {
var simpleClass:SimpleClass?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Second Class";
if let passedSimpleClass = self.simpleClass {
println(passedSimpleClass.address)
}
}
}

If you want the values from the ViewController to your SecondView you have to implement
override func prepareForSegue(segue: UIStoryboardSegue?, sender:
AnyObject?)
You're getting the right value for temp.username which is nil displayed in log when you go to the Secondview. This is because you're creating a new instance of SimpleClass who's variables are initialised to nil.

Related

Delegate function not called

I have two view controllers (ViewController and ActionViewController) and one manager (Brain), the second view controller is shown when a user tapped on a button by a show segue created in storyboard and to get back to the first I use a self.dismiss in the second view controller.
The user enter a number on ActionViewController that need to be retrieved in ViewController. So I created Brain to use the delegate pattern.
The problem is that the delegate function inside ViewController is never run, I read other SO answers but nothing work. I used print statement to know where the code is not running anymore and the only block not running is the didUpdatePrice inside ViewController
Here is the code
ViewController
class ViewController: UIViewController, BrainDelegate {
var brain = Brain()
#IBOutlet var scoreLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
brain.delegate = self
scoreLabel.layer.cornerRadius = 25
scoreLabel.layer.masksToBounds = true
}
func didUpdateScore(newScore: String) {
print("the new label is \(newScore)")
scoreLabel.text = newScore
}
}
ActionViewController
class ActionViewController: UIViewController {
var brain = Brain()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addButtonTapped(_ sender: Any) {
brain.newAction(actualScore: 0, newActionValue: 5, isPositive: true)
self.dismiss(animated: true)
}
}
Brain
protocol BrainDelegate {
func didUpdateScore(newScore: String)
}
struct Brain {
var delegate: BrainDelegate?
func newAction(actualScore: Int, newActionValue: Int, isPositive: Bool) {
let newScore: Int
if isPositive {
newScore = actualScore + newActionValue
} else {
newScore = actualScore - newActionValue
}
print("the new score is \(newScore)")
delegate?.didUpdateScore(newScore: String(newScore))
}
}
You dont need an additional Brain class/struct at all, You can achieve it with simple protocol and default extension of protocol.
Step 1: Select your show segue and provide an identifier to that in storyboard as shown below
Step 2: In your ViewController add prepare(for segue method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "testIdentifier" {
guard let destinationViewController = segue.destination as? ActionViewController else { return }
destinationViewController.delegate = self
}
}
Step 3: In your ActionViewController declare a weak property named delegate
class ActionViewController: UIViewController {
weak var delegate: BrainDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addButtonTapped(_ sender: Any) {
delegate?.newAction(actualScore: 0, newActionValue: 5, isPositive: true)
self.dismiss(animated: true)
}
}
Step 4: Add class clause to your BrainDelegate (Class bound protocol) so that you can hold a weak reference to delegate
protocol BrainDelegate: class {
func didUpdateScore(newScore: String)
func newAction(actualScore: Int, newActionValue: Int, isPositive: Bool)
}
Step 5:
Add a default extension to BrainDelegate and provide default implementation of newAction(actualScore:
extension BrainDelegate {
func newAction(actualScore: Int, newActionValue: Int, isPositive: Bool) {
let newScore: Int
if isPositive {
newScore = actualScore + newActionValue
} else {
newScore = actualScore - newActionValue
}
print("the new score is \(newScore)")
self.didUpdateScore(newScore: String(newScore))
}
}
Step 6: In your ActionViewController simply trigger delegate methods as
#IBAction func addButtonTapped(_ sender: Any) {
delegate?.newAction(actualScore: 0, newActionValue: 5, isPositive: true)
self.dismiss(animated: true)
}
This should do the job
First, on Brain you should use class, not struct. That is because when you use struct, passing the variable to another will make a copy, it will not use the same reference. And class will only copy the reference.
That means that your Brain struct will lose the delegate assigned on .delegate = self
second, you need to use the same instance on the second viewController and the first. like this:
on the first viewController
var brain = Brain()
// this one is the one that you will put your "brain.delegate = self"
on the second viewController, you will need to inject this variable from the first viewController into the second. That is to keep the same instance on both. And this will make the delegate callable.
to do this with storyboard you will do on the first ViewController:
// this function should be called when the next viewController should open.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination) {
case let vc as MyViewController:
vc.brain = self.brain
default:
break
}
}
super.prepare(for: segue, sender: sender)
}
inside the second viewController, use:
var brain: Brain?

Receive changing data into another controller Swift

I have a ViewController with a variable which value is changing every second ( from a sensor ).
I made another ViewController let's call it SensorViewController with a Label on the screen in which I want to display the value from the main ViewController.
If I use override func prepare(for segue: UIStoryboardSegue, sender: Any?) the value is send but only one time ( it doesn't refresh/update every second ).
What can I do to change the value from SensorViewController every time the value from ViewController is changing?
Example:
// ViewController example code:
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
distanta1 = String(byteArray[0]) // variable which is changing every second
#IBAction func distantaSenzori(_ sender: UIButton) { //button which send me to SensorViewController
self.performSegue(withIdentifier: "goToSenzori", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // this one is sending value only when I press the button from above ( I have to exit from SensorViewController and enter again to see updated value )
if segue.identifier == "goToSenzori"{
let destinatieVC = segue.destination as! SensorViewViewController
destinatieVC.distance1 = distanta1 } }
}
// SensorViewController code:
class SensorViewViewController: UIViewController {
#IBOutlet weak var distanta1: UILabel!
var distance1: String?
override func viewDidLoad() {
super.viewDidLoad()
distanta1.text = distance2 }
}
Thank you very much, guys! You are awesome!
For Frankenstein:
In class SensorViewViewController my code looks like that:
var distance1: String?
override func viewDidLoad() {
super.viewDidLoad()
distanta1.text = distance1
print("Distance 1 is \(distance1)")
// Do any additional setup after loading the view.
}
It's called only once and the value is nil. What should I modify at the code here so the value to be refreshed?
I think a cleaner solution is to create a shared manager for handling the sensor. After that, you can notify your objects about the changing value. Of course in your case your "sensor" is something bluetooth but what I wrote is only a template basically, you can fill in your necessary methods and objects, delegates, so on.
class SensorManager {
static let shared: SensorManager = SensorManager()
private var sensor: Sensor
private init() {
sensor = Sensor()
}
//MARK: - Public methods
func startTheSensor() {
//This is what you call to start your sensor
}
func getSensorData() -> YourData {
//This is from where your objects can read the sensor data
}
//MARK: - Private methods -
private func didSensorUpdatedValue() {
//This is called whenever your sensor updates
.
.
.
let newSensorValue = "yourValue"
NotificationCenter.default.post(name: .init("SensorDataChanged"), object: nil)
}
}
In your viewcontroller:
deinit() {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
weak var this = self
NotificationCenter.default.addObserver(this, selector: #selector(didSensorValueChanged), name: .init("SensorDataChanged"), object: nil)
}
#objc func didSensorValueChanged() {
SensorManager.shared.getSensorData()
}
You need to add an observer and keep reference to the destination view controller to keep passing in the new value that has been changed to the destination view controller. Here's how:
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var distanta1 = String(byteArray[0]) {
didSet {
destinatieVC?.distance1 = distanta1
}
}
//...
var destinatieVC: SensorViewViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToSenzori" {
destinatieVC = segue.destination as? SensorViewViewController
}
}
}
In SensorViewViewController:
var distance1: String? {
didSet {
print(distance1 ?? "")
}
}
Better approach: Set the destinatieVC?.distanta1.text = distanta1 directly if you're not doing anything else in the didSet block avoid the distance1 property entirely.

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)
}
}

Passing data from view controller to another view controller

I have not found an exact solution for my problem. I need to pass data from one view controller to another view controller. The problem is, that after the segue, the passed string data does not appear in the label.
func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject) {
if (segue.identifier == "segue1") {
if let destination = segue.destination as? ResultsViewController {
destination.name = correctslabel.text!
The second controller: there is just variable and "name" and a UIlabel, which does not show the passed data.
import UIKit
class ResultsViewController: UIViewController {
#IBOutlet var namelabel: UILabel!
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
name = namelabel.text!
}
}
I've tried many ways to do it, but none of them worked. Thank you
Finally worked for me to do an override func of it, which meant, that I had to change the sender from AnyObject to Any?.
Haven't you tried this?
// ResultsViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
namelabel.text = name
}

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

Resources