Delegation between two ViewControllers without segues - ios

I am learning the concept of delegation and I am stuck in my project. I believe the solution is simple, but as this is something new for me I have no idea what is wrong.
Project concept is simple:
There are two views in the application. In the first view you press the button "change the color" and as a result second view appears. In the second view there are three text fields where user puts numbers respectively for R G and B values in RGB color. When the button is tapped, second view disappears and the first view's background should be changed with color based on the user's input. I assume that the user put correct numbers, therefore at this moment I use forced unwrapping for those values.
At this moment views appear correctly, but the background color of the first view does not change and I have no idea why.
Beneath is the code for two view controllers. I will appreciate any hints.
First VC: ViewController.swift
class ViewController: UIViewController, ColorChangeDelegate {
let secondStoryboard = UIStoryboard(name: "Second", bundle: nil).instantiateViewControllerWithIdentifier("SecondViewController") as UIViewController
var secondVC = SecondViewController()
#IBAction func changeColourButtonTapped(sender: UIButton) {
presentViewController(secondStoryboard, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
secondVC.colorDelegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func didChangeColor(controller: SecondViewController, color: UIColor) {
self.view.backgroundColor = color
}
}
and second VC: SecondViewController.swift
protocol ColorChangeDelegate {
func didChangeColor(controller: SecondViewController, color: UIColor)
}
class SecondViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var myRTextField: UITextField!
#IBOutlet weak var myGTextField: UITextField!
#IBOutlet weak var myBTextField: UITextField!
var colorDelegate : ColorChangeDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myRTextField.delegate = self
myGTextField.delegate = self
myBTextField.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldDidBeginEditing(textField: UITextField) {
textField.becomeFirstResponder()
}
#IBAction func goButtonTapped(sender: UIButton) {
let r = Int(myRTextField.text!)!
let g = Int(myGTextField.text!)!
let b = Int(myBTextField.text!)!
let color = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1.0)
self.view.endEditing(true)
colorDelegate?.didChangeColor(self, color: color)
presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}
}

You are assigning your delegate to secondVC but the view controller that is actually being presented is in secondStoryboard, so you should set the colorDelegate property of secondStoryboard.

Related

How to add and delete segments in a viewcontroller by clicking add button an delete button created in the same viewcontroller?

import UIKit
class ViewController: UIViewController {
// #IBAction func Btndel(_ sender: Any) {
//}
var Str:String?
override func viewDidLoad() {
super.viewDidLoad()
let items = [Str]
let SegM = UISegmentedControl(items:items as Any as? [Any])
SegM.selectedSegmentIndex = 0
SegM.frame=CGRect(x: 70, y: 130, width: 100, height: 50)
SegM.layer.cornerRadius = 8.0
SegM.backgroundColor = .orange
SegM.tintColor = .white
self.view .addSubview(SegM)
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func Btnadd(_ sender: Any)
{
var Str = 0;Str += 1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
How to add and delete segments in a viewcontroller by clicking add button an delete button created in the same viewcontroller
How to insert and remove(add or delete) segments in swift4
class ViewController: UIViewController {
#IBOutlet weak var segment1: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func insert(_ sender: Any) {
segment1.insertSegment(withTitle: "\(segment1.numberOfSegments+1)", at: segment1.numberOfSegments, animated: true)
}
#IBAction func remove(_ sender: Any) {
segment1.removeSegment(at: segment1.numberOfSegments-1, animated: true)
}
}
You can insert segment by insertSegment method of UISegmentedControl and you can delete segment by removeSegment method. Let me take an example.
I create segmentController class and its UI in a storyboard.
Below is UI screenshot. In the storyboard, You can see two buttons Insert (+) and Remove (-) and UISegmentedControl. Insert button will insert segment at a specific position and Remove button will remove the segment at a specific position.
Below is code of segmentController class.
class segmentController: UIViewController {
#IBOutlet weak var segementControl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func remove(_ sender: Any) {
segementControl.removeSegment(at: segementControl.numberOfSegments-1, animated: true)
}
#IBAction func insert(_ sender: Any) {
segementControl.insertSegment(withTitle: "\(segementControl.numberOfSegments+1)", at: segementControl.numberOfSegments, animated: true)
}
}
In the above code, on insert button click new segment will add at the last of segementControl. On remove button click the last segment will delete from segmentControl.
Hope it helps.

Cannot unwrap optional value to label

I am new to this board. Please, excuse my bad english in advance.
I am trying to send a string from a subview to his parent view. If I try to set that string to a label, my app crashes with the message "unexpectedly found nil while unwrapping an Optional value".
Example code from the subview:
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blueColor()
sendDataToVc("test")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func sendDataToVc(myString : String) {
let Vc = ViewController()
Vc.dataFromContainer(myString)
}
Example from the parent view:
class ViewController: UIViewController {
#IBOutlet weak var label1: UILabel!
var cacheStr1 : String!
var cacheStr2 : String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
label1.text = ""
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func dataFromContainer(containerData : String){
label1.text = cacheStr1
}
#IBAction func changeLabel(sender: AnyObject) {
}
I have no more ideas what I am doing wrong. Thank you for your help.
The problem is this line:
let Vc = ViewController()
You are creating a new instance — a new ViewController instance. That's not what you want to do. You want to get a reference to an existing instance — the one that is your view controller's parent view controller, if that's what a View Controller is in relation to your TableViewController.
You better instance your ViewController form StoryBoard and define what you want to pass as property, and then set this property to the value that you need to show, and in the viewDidLoad of your ViewController update your view as you need

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

Container view losing textField values when a button is tapped in the parent viewController

I have a viewController with a UISegmentedControl and a UIButton.
Within this viewController, I have two containers, each containing one viewController with a UITextField inside.
I want to save the values in the textField on the click of the button.
Here's the code I have written so far:
View Controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//
//
containerA.showView()
containerB.hideView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonTapped(sender: UIButton) {
print(ContainerAViewController.sharedInstance.textFieldA)
}
#IBAction func segmentedControlValueChanged(sender: AnyObject) {
switch(sender.selectedSegmentIndex) {
case 0 : containerA.showView()
containerB.hideView()
case 1 : containerB.showView()
containerA.hideView()
default : containerA.showView()
containerB.hideView()
}
}
#IBOutlet weak var containerA: UIView!
#IBOutlet weak var containerB: UIView!
func hideView(view: UIView) {
view.userInteractionEnabled = false
view.hidden = true
}
func showView(view: UIView) {
view.userInteractionEnabled = true
view.hidden = false
}
}
extension UIView {
func hideView() -> UIView {
self.userInteractionEnabled = false
self.hidden = true
return self
}
func showView() -> UIView {
self.userInteractionEnabled = true
self.hidden = false
return self
}
}
ContainerAViewController:
class ContainerAViewController: UIViewController {
#IBOutlet weak var textFieldA: UITextField!
static let sharedInstance = ContainerAViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
ContainerBViewController:
class ContainerBViewController: UIViewController {
#IBOutlet weak var textFieldB: UITextField!
static let sharedInstance = ContainerBViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
When I tap the button, it gives me the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Can somebody please help?
You should not try to manipulate another view controller's views. That violates the principle of encapsulation, an important principle in object-oriented development.
You should give your child view controllers (ContainerAViewController and ContainerBViewController) string properties, and have the code for those view controllers set that string property when the user enters text into the view controllers' text fields.
Ignoring that design flaw, your code doesn't make sense. You show your CLASS as ContainerAViewController, and yet your buttonTapped method is trying to ask a ContainerAViewController singleton for the value of a text field. That makes no sense.
You want to have properties in your parent view controller that point to your child view controllers.
You should implement a prepareForSegue method in your parent view controller, and in that prepareForSegue method, look for the embed segues that fire when the child view controllers are loaded. When that happens you should set your properties that point to the child view controllers.

Changing label text inside method

I'm using delegation to pass back information from a view controller.
This is the method
func writeValueBack(value: String) {
self.label.text = value
}
The function gets called and all is great apart from the label doesn't update.
The label has a value and is not returning nil, I checked with this line
println(self.label.text)
It prints the value of 'value'
So that means that the label's text is being set to 'value' but it's not updating.
I even tried using the main thread but no luck
func writeValueBack(value: String) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.label.text = value
})
}
I just don't know what the problem is.
Any help would be great.
Protocol:
protocol writeValueBackDelegate {
func writeValueBack(value: String)
}
EDIT:
Code for my view controller:
//
// ViewController.swift
// DelegateTesting
//
// Created by Alex Catchpole on 30/11/2014.
// Copyright (c) 2014 Alex Catchpole. All rights reserved.
//
import UIKit
class ViewController: UIViewController, writeValueBackDelegate {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "MainSegue" {
var vc = segue.destinationViewController as SecondViewController
vc.labelText = textField.text
vc.delegate = self
}
}
func writeValueBack(value: String) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.label.text = value
})
}
#IBAction func button(sender: AnyObject) {
self.label.text = textField.text
}
#IBAction func segue(sender: AnyObject) {
performSegueWithIdentifier("MainSegue", sender: self)
}
}
Second ViewController source:
//
// SecondViewController.swift
// DelegateTesting
//
// Created by Alex Catchpole on 30/11/2014.
// Copyright (c) 2014 Alex Catchpole. All rights reserved.
//
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var textField: UITextField!
var labelText: String!
var delegate: writeValueBackDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
label.text = labelText
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func button(sender: AnyObject) {
label.text = textField.text
}
#IBAction func segueBack(sender: AnyObject) {
var editedText = label.text
performSegueWithIdentifier("SecondSegue", sender: self)
if (delegate != nil) {
delegate?.writeValueBack(editedText)
println("working")
}
}
}
The issue is in your second view controller, which as rdelmar pointed out creates a new instance of your first view controller instead of navigating back to the original instance.
To fix this, you could use dismissViewControllerAnimated:completion instead of performing your second segue. But an unwind segue would lead to simpler code, and can be achieved by adding this to your first view controller:
#IBAction func unwindFromSecond(unwindSegue: UIStoryboardSegue) {
if let secondViewController = unwindSegue.sourceViewController as? SecondViewController {
label.text = secondViewController.label.text
// Or whatever you need to retrieve data from the second controller
}
}
Then in your storyboard create an unwind segue from the second view controller. For example if you have a Dismiss button, control-drag from this button to the Exit icon in your second view controller scene and choosing unwindFromSecond. For detailed steps see the answer to this other question: What are Unwind segues for and how do you use them?
You can now remove the writeValueBackDelegate declaration and associated variables, the writeValueBack method and the second view controller's segueBack method

Resources