My delegate method becomes nil while it shouldn't? - swift

got 2 ViewControllers 1st is ViewController 2nd TableViewCotnroller
class ViewController: UIViewController, CLLocationManagerDelegate, TabVCDelegate {
func reciveData(_ numberOfRows: Int) {
print(numberOfRows)
}
...
}
TableViewController:
protocol TabVCDelegate {
func reciveData(_ numberOfRows: Int)
}
class TabVC: UITableViewController {
var delegate: TabVCDelegate?
#IBAction func passDataBack(_ sender: UIButton) {
delegate?.reciveData(5)
self.dismiss(animated: true, completion: nil)
print(delegate ?? "show me if its nil")
}
my delegate?.reciveData(5) is for some reason nil can't figure it out
it did worked when i had 2 normal ViewController am i missing something about TableViewControllers? or maybe its something else?
any help would be appreciated.

First of:
Make that delegate property weak to avoid strong reference cycle
weak var delegate: TabVCDelegate?
To achieve that your protocol should conform to class
protocol TabVCDelegate: class {
func reciveData(_ numberOfRows: Int)
}
Next:
You must set that delegate somewhere. If you have reference to TabVC instance in your ViewController class then it would look like this:
tabVC.delegate = self
HERE is detailed description about "how to create delegates in Swift"

thanks
actually i did found what was missing, in segue i forgot to set destinationVC.delegate as self when setting segue
:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToTableView"{
let destinationVC = segue.destination as! TabVC
destinationVC.delegate = self // <-- this line was missing
destinationVC.dataPassedThroughSegue = locationLabel.text
}
}

Related

Error in calling method from another class when using delegates

I have tried to run Second ViewController to change appearance on my first ViewController and progressBar(Progress View) cause error, but if I try to call method keyButtonResponse() from First View Controller error doesn`t appear
First ViewController:
import UIKit
import AVFoundation
import UserNotifications
protocol SomeDelegate {
func keyButtonResponse()
}
class FirstViewController: UIViewController, SomeDelegate{
#IBOutlet weak var label: UILabel!
#IBOutlet weak var progressBar: UIProgressView!
func keyButtonResponse(){
//some code
progressBar.progress = 0.01 //Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
}
}
Second ViewController:
class SecondViewController: UIViewController {
var delegate: SomeDelegate?
func keyButtonResponse(){
delegate?.keyButtonResponse()
}
#IBAction func leftButtonPressed(_ sender: UIButton) {
let myVar = WarningViewController()
let viewController = ViewController()
myVar.delegate = viewController
myVar.keyButtonResponse()
}
}
You're setting the delegate to an instance of a view controller that's unrelated to FirstViewController here.
let viewController = ViewController()
myVar.delegate = viewController
You need to set the delegate to the existing instance of FirstViewController.
If you're using a segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? SecondViewController, segue.identifier == "firstVCToSecondVCSegue" {
vc.delegate = self
}
}
or
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController {
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
This should be placed within FirstViewController, not SecondViewController.

i cannot able to pass data between viewcontrollers via protocols

View controller A
class ViewController: UIViewController {
var delegate: server?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func ok(_ sender: Any) {
delegate?.datum(data: "sd")
performSegue(withIdentifier: "goingB", sender: self)
}
}
View controller B
protocol server {
func datum(data: String)
}
class ViewControllerB: UIViewController, server {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func datum(data: String) {
self.label.text = data
print(data)
}
}
I need to pass the data via view controllers but I cannot able to pass however I know we can pass data through protocols, but anyhow I end up with error when try to run the program
If you need to pass data from one view controller to another and you're using segue for presenting new view controller, you can just override prepare(for:sender:), there is no need to using delegates. Here you can get reference for controller which will be presented and you can assign its variable.
So, first create variable in second view controller and declare that if you assign it with new value, it changes text of your label
class ViewControllerB: UIViewController {
#IBOutlet weak var label: UILabel!
var variable: String? {
didSet {
label.text = variable
}
}
}
Now in first view controller override prepare(for:sender:) and if segue is segue which you've performed, downcast destination view controller and assign its variable
class ViewController: UIViewController {
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goingB" {
let destinationVC = segue.destination as! ViewControllerB
destinationVC.variable = "sd"
}
}
}
Anyway, if you want to use your code with delegate, you have to set delegate of first view controller as second view controller which will be presented. For this purpose you can also use prepare(for:sender:) where you can get reference for destination of segue and then you can call your method on delegate
class ViewController: UIViewController {
var delegate: server?
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goingB" {
let destinationVC = segue.destination as! ViewControllerB
delegate = destinationVC
delegate?.datum(data: "sd")
}
}
}
Notes:
Name protocol with big capital letter Server and we are talking about delegates, add delegate word: ServerDelegate
Constrain protocol for just for classes
Make then your delegate variable weak
protocol ServerDelegate: class {
func datum(data: String)
}
...
weak var delegate: ServerDelegate?
The simplest here is to to set the property directly in prepare.
However, if you want to use delegate, you can. Your problem is that you have mixed between A and B.
The way you wrote it, when you call delegate?.datum, delegate is not defined and we can't access datum.
What do you want to do ? Go from A to B, and when in B, update a label in B with data received from A.
Here just to show how to use (but clearly too complex compared with direct assignment).
protocol Server {
func datum() -> String
}
class ViewControllerB: UIViewController {
#IBOutlet weak var label: UILabel!
var delegate: Server?
override func viewDidLoad() {
super.viewDidLoad()
let data = delegate?.datum()
self.label.text = data
}
}
class ViewControllerA: UIViewController, Server {
override func viewDidLoad() {
super.viewDidLoad()
}
var data = "sd"
func datum() -> String {
return data
}
#IBAction func ok(_ sender: Any) {
performSegue(withIdentifier: "goingB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destVC = segue.destination as? ViewControllerB {
destVC.delegate = self
}
}
}

Can't pass value from FirstVC to SecondVC using segue

I have two ViewControllers connected via Show segue. I need to pass NSSlider's value from ViewController to SecondViewCotroller.
So, moving slider in ViewController a variable updates in SecondViewController.
How to update a value of imagesQty variable?
// FIRST VIEW CONTROLLER
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var slider: NSSlider!
#IBOutlet weak var photosLabel: NSTextField!
#IBAction func segueData(_ sender: NSSlider) {
photosLabel.stringValue = String(slider.intValue) + " photos"
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "SegueIdentifierForSecondVC"), sender: slider)
}
func prepare(for segue: NSStoryboardSegue, sender: NSSlider?) {
if segue.identifier!.rawValue == "SegueIdentifierForSecondVC" {
if let secondViewController =
segue.destinationController as? SecondViewController {
secondViewController.imagesQty = slider.integerValue
}
}
}
}
and
// SECOND VIEW CONTROLLER
import Cocoa
class SecondViewController: NSViewController {
var imagesQty = 30
override func viewWillAppear() {
super.viewWillAppear()
self.view.wantsLayer = true
print("viewWillAppear – Qty:\(imagesQty)")
//let arrayOfViews: [NSImageView] = [view01...view12]
let url = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Desktop/ArrayOfElements")
do {
let fileURLs = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]).reversed()
let photos = fileURLs.filter { $0.pathExtension == "jpg" }
for view in arrayOfViews {
//"imagesQty" is here
let i = Int(arc4random_uniform(UInt32(imagesQty-1)))
let image = NSImage(data: try Data(contentsOf: photos[i]))
view.image = image
view.imageScaling = .scaleNone
}
} catch {
print(error)
}
}
First of all the purpose and benefit of NSStoryboardSegue.Identifier is to create an extension to be able to avoid literals.
extension NSStoryboardSegue.Identifier {
static let secondVC = NSStoryboardSegue.Identifier("SegueIdentifierForSecondVC")
}
Then you can write
self.performSegue(withIdentifier: .secondVC, sender: slider)
and
if segue.identifier! == .secondVC { ...
This error occurs because imagesQty is declared in viewWillAppear rather than on the top level of the class.
Change it to
class SecondViewController: NSViewController {
var imagesQty = 30 // Int is inferred
// override func viewWillAppear() {
// super.viewWillAppear()
// }
}
There is another mistake: The signature of prepare(for segue is wrong. It must be
func prepare(for segue: NSStoryboardSegue, sender: Any?) {
You can‘t change the value because the var is defined in the function and not in the class.
Make your var a class property and it should work.
class SecondViewController: UIViewController {
var imagesQty: Int = 30
...
}

Swift Thread 1: signal SIGART error after adding a new delegate

I’m new to Swift and have been stuck on this problem for a while.
I’m trying to add a ‘settings’ view where the size of the font can be updated for the main view.
I get a Thread 1: signal SIGART error on the line: class AppDelegate: UIResponder, UIApplicationDelegate
After adding the below code, my app now crashes when I press on the settings button.
Not sure how else to debug this problem, any help will be much appreciated! Thanks in advance.
View Controller:
class ViewController: UIViewController, CLLocationManagerDelegate, SettingsViewControllerDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SettingsViewController" {
let settingsViewController = segue.destination as? SettingsViewController
if let viewController = settingsViewController {
viewController.delegate = self
}
}
}
func makeChangesToFont(size: Int) {
if (self.distanceReading.font.pointSize > 20) {
self.distanceReading.font = UIFont(name: self.distanceReading.font.fontName, size: CGFloat(size))
}
}
...
}
Settings Controller:
import UIKit
protocol SettingsViewControllerDelegate {
func makeChangesToFont(size: Int)
}
class SettingsViewController: UIViewController {
var delegate: SettingsViewControllerDelegate?
#IBAction func IncreaseSize(_ sender: UIButton) {
print("something")
increaseFontSize()
}
func increaseFontSize() {
let mainView = parent as! ViewController
let text = mainView.distanceReading
let fontSize = (text?.font.pointSize)! + 10
print(fontSize)
if let delegate = self.delegate {
delegate.makeChangesToFont(size: Int(fontSize))
}
}
...
}
I'm using Swift 3 in Xcode 8.2.1

Delegate using Container View in Swift

I'm developing an app for iPad Pro. In this app, containerView use to add additional views and interact with them.
First, I created a protocol:
protocol DataViewDelegate {
func setTouch(touch: Bool)
}
Then, I created my first view controller
import UIKit
class ViewController: UIViewController, DataViewDelegate {
#IBOutlet var container: UIView!
#IBOutlet var labelText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func setTouch(touch: Bool) {
if touch == true {
labelText.text = "Touch!"
}
}
}
And finally, I created a view that will be embedded in containerView.
import UIKit
class ContainerViewController: UIViewController {
var dataViewDelegate: DataViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func touchMe(sender: AnyObject) {
dataViewDelegate?. setTouch(true)
}
}
But for some reason, nothing happened, the first view controller receives nothing in setTouch function.
My question is: In this case, using container, how can I make the communication between two ViewsControllers?
Like #nwales said you haven't yet set the delegate. You should do set the delegate in prepareForSegue function on your first viewController (who contain the viewContainer)
First select the embed segue and set an identifier in the attributes inspector.
Then in the parentViewController implement the func prepareForSegue like this:
Swift 4+:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "the identifier") {
let embedVC = segue.destination as! ViewController
embedVC.delegate = self
}
}
Below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if (segue.identifier == "the identifier") {
let embedVC = segue.destinationViewController as! ContainerViewController
embedVC.dataViewDelegate = self
}
}
Looks like you defined the delegate, but have not set the delegate. This happens to me all the time.