Error in calling method from another class when using delegates - swift

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.

Related

My delegate method becomes nil while it shouldn't?

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

Swift 4 Delegate doesn't work from childViewController

I have to code simple program. Just one login screen waiting for some password and after setting proper one, change screen to "program itself" and its features.
I decided to do it by container View and its subviews. everything works fine but delegate which should be responsible for triggering this change doesn't respond. In any option I tried, nothing has changed... it still doesn't respond.
This is code of ViewController (main):
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var container: NSView!
var vc1 : ViewController1 = ViewController1()
var vc2 : ViewController2 = ViewController2()
override func viewDidLoad() {
super.viewDidLoad()
//initialise of delegate:
vc1.delegate = self
// set subviews:
vc1 = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "ViewController1")) as! ViewController1
vc2 = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "ViewController2")) as! ViewController2
self.addChildViewController(vc1)
self.addChildViewController(vc2)
// set first view:
vc1.view.frame = self.container.bounds
self.container.addSubview(vc1.view)
}
func changeViews()
{
for sView in self.container.subviews {
sView.removeFromSuperview()
}
// set second view:
vc2.view.frame = self.container.bounds
self.container.addSubview(vc2.view)
}
}
extension ViewController: ViewController1_Delegate {
func passwdEntered(_ correctPasswd: Bool) {
if correctPasswd == true{
changeViews()
}
}
}
and this is First (SubView) ViewController code, where I'm entering passwd:
import Cocoa
protocol ViewController1_Delegate: class {
func passwdEntered(_ correctPasswd: Bool)
}
class ViewController1: NSViewController{
#IBOutlet weak var passwordField: PasswordField!
weak var delegate: ViewController1_Delegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func passwordFIeldEdited(_ sender: PasswordField) {
delegate?.passwdEntered(true/*for testing purpose always true*/)
}
}
Finally Second (SubView) ViewController code:
import Cocoa
class ViewController2: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
What I'm doing wrong?
First you say this:
var vc1 : ViewController1 = ViewController1()
override func viewDidLoad() {
super.viewDidLoad()
vc1.delegate = self
Then you say this:
vc1 = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"),
bundle: nil).instantiateController(
withIdentifier: NSStoryboard.SceneIdentifier(
rawValue: "ViewController1")) as! ViewController1
So, you set yourself as delegate on a ViewController1 instance, but then, in the very next line, you throw away that ViewController1 and replace it with another!
Move the line v1.delegate = self down to after you set the real value of vc1.

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

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.

Swift: Pass multiple values between views

I have a view that has two text fields and a button.
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
}
and a second view that has two variables:
var submittedURL = ""
var submittedName = ""
println("Name \(submittedName)")
println("URL \(submittedURL)")
In Swift How do I pass the values entered in the two text fields and assign them to those variables in the second view?
Thanks
EDIT FOR THETOM:
import UIKit
class ViewController: UIViewController {
#IBOutlet var inputURL: UITextField!
#IBAction func submitBtn(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as BrandsViewController
destinationVC.submittedURL.text = inputURL.text
}
}
You can use the method prepareForSegue.
In the first view (the one from which the segue is coming from) write the following code :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as CustomViewController
destinationVC.submittedURL = inputURL.text
destinationVC.submittedName = inputName.text
}
Here CustomViewController is the custom class of the UIViewController to which the segue is going to.
To perform the segue programmatically in your button #IBAction do that :
#IBAction func buttonWasClicked(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
Since your view controllers are linked with segue you can override the prepareForSegue method in first view controller and pass data by doing so
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "secondViewController") { // here secondViewController is your segue identifier
var secondViewController = segue.destinationViewController as SecondViewController // where SecondViewController is the name of your second view controller class
secondViewController.submittedURL = inputURL.text
secondViewController.submittedName = inputName.text
}
}
And to performSegue inside your button action use perfromSegueWithIdentifier method
#IBAction func submitUrlButton(sender: AnyObject) {
//replace identifier with your identifier from storyboard
self.performSegueWithIdentifier("secondViewController", sender: self)
}
The simplest way of accessing values globally not neccessary to pass with segue
First View controller
import UIKit
var submittedURL:NSString? // declare them here
var submittedName:NSString? // Now these two variables are accessible globally
class YourViewController : UIViewController
{
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
if inputURL.text == "" && inputName.text == ""
{
//Show an alert here etc
}
else {
self.submittedURL.text = inputURL.text
self.submittedName.text = inputName.text
}
}
}
SecondView Controller
import UIKit
class SecondviewController: UIViewController
{
//inside viewDidload
println(submittedURL)
println(submittedName)
}