I am using delegates to get a string value from my modal. When the modal closes I am trying to update Label text using that string. However, I am getting error: Unexpectedly found nil while implicitly unwrapping an Optional value: file. I am not sure how to fix this. I think it's happening because the view is not yet active.
import Cocoa
class ViewControllerA: NSViewController, SomeDelegate {
#IBOutlet weak var msgLabel: NSTextField!
var s: String = "";
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func setDetails(s: String) {
self.user = s;
print("Notified", self.s) // <-- prints: Notified hello again
msgLabel.stringValue = self.s <-- DOESN'T WORK
}
func showModal() -> Void {
msgLabel.stringValue = "hello" // <--- WORKS
let cbvc: NSViewController = {
return self.storyboard!.instantiateController(withIdentifier: "ControllerBVC")
as! NSViewController
}()
self.presentAsModalWindow(cbvc);
}
#IBAction func onBtn(_ sender: Any) {
self.showModal();
}
}
protocol SomeDelegate {
func setDetails(s: String)
}
class ViewControllerB: NSViewController {
#IBOutlet weak var textF: NSTextField!
var delegate: SomeDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
let vc = ViewControllerA()
self.delegate = vc
}
#IBAction func onBtn(_ sender: Any) {
DispatchQueue.main.async {
self.delegate?.setDetails(s: self.textF.stringValue)
self.dismiss("ControllerAVC")
}
}
}
You have a number of problems.
In ViewControllerB.viewDidLoad you are assigning a new instance of ViewControllerA to the delegate property. Don't do that. Your viewDidLoad method should look like this:
override func viewDidLoad() {
super.viewDidLoad()
}
In the showModal method ViewControllerA should assign itself as the delegate on ViewControllerB before ViewControllerB it is presented.
func showModal() -> Void {
let cbvc: NSViewController = {
let vc = self.storyboard!.instantiateController(withIdentifier: "ControllerBVC")
as! ViewControllerB
vc.delegate = self
return vc
}()
self.presentAsModalWindow(cbvc);
}
In the setDetails method just assign the string to your text field directly:
func setDetails(s: String) {
msgLabel.stringValue = s
}
Related
How do I use delegates to send data to another view controller and then display it in the collection view? My problem is with moving the array across using delegates.
Below is an example of what I am working on.
When I use usersList in the ThirdViewController, I get an error that says 'Unexpectedly found nil while implicitly unwrapping an Optional value'
protocol ExampleDelegate {
func delegateFunction(usersArray: Array<User>)
}
class ViewController: UIViewController {
private var model: Users = ViewController.createAccount()
var exampleDelegate: ExampleDelegate?
#IBAction func ShowUsers(_ sender: UIButton) {
let ShowUsersVC = storyboard?.instantiateViewController(identifier: "ThirdViewController") as! ThirdViewController
var userList: Array<User> = model.listOfUsers
exampleDelegate?.delegateFunction(usersArray: userList )
present(ShowUsersVC, animated: true)
}
}
class ThirdViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var usersList: Array<User>!
override func viewDidLoad() {
super.viewDidLoad()
let GetUsersVC = storyboard?.instantiateViewController(identifier: "ViewController") as! ViewController
GetUsersVC.showMomentsDelegate = self
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ThirdViewController: ExampleDelegate {
func delegateFunction(usersArray: Array<User>)
usersList = usersArray
}
You don't need delegates in this case. You are sending data forwards, so just do it like this:
class ViewController: UIViewController {
private var model: Users = ViewController.createAccount()
var exampleDelegate: ExampleDelegate?
#IBAction func showUsers(_ sender: UIButton) {
let showUsersVC = storyboard?.instantiateViewController(identifier: "ThirdViewController") as! ThirdViewController
var userList: Array<User> = model.listOfUsers
showUsersVC.usersList = userList /// pass the data!
present(showUsersVC, animated: true)
}
}
Also in Swift you should lowercase objects like userList, as well as functions like showUsers.
I am trying to make a list of users and their passwords in one view controller, save that information in a dictionary, and send that dictionary to another view controller which asks the user to input their username/password combination to authorize the log in. (the key is the username and the value is the password). Is there a way I can send the dictionary from SecondVC to the FirstVC?
First View Controller
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var Username: UITextField!
#IBOutlet weak var Verification: UILabel!
#IBOutlet weak var Password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Username.delegate = self
Password.delegate = self
}
var usersDict = [String : String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = usersDict
}
}
#IBAction func Authorization(_ sender: Any) {
for ( key , value ) in usersDict{
let v = key.count
var start = 0
if start <= v{
if Username.text == key{
if Password.text == value{
Verification.text = "Looks Good"
}
}
else{
start += 1
}
}
else{
Verification.text = "Yikes"
}
}
}
}
Second View Controller
class AccountViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var CreateUsername: UITextField!
#IBOutlet weak var CreatePassword: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
CreateUsername.delegate = self
CreatePassword.delegate = self
// Do any additional setup after loading the view.
}
var usersDict = [ String : String ]()
#IBAction func MakeANewAccount(_ sender: Any) {
usersDict[CreateUsername.text!] = CreatePassword.text!
}
}
I have made there dictionary, but it will only send in the beginning and won't update after creating the original account. (dictionary it is sending is empty)
With a segue add this method inside ViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = yourDicHere
}
}
Here's a general pattern for making a controller work with data from some object it creates, in this case a second controller.
Try applying it to your situation and let me know if you run into problems.
protocol Processor {
func process(_ dict: [String : String])
}
class FirstController: UIViewController, Processor {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? SecondController {
controller.delegate = self
} else {
print("Unexpected view controller \(segue.destination)")
}
}
func process(_ dict: [String : String]) {
}
}
class SecondController: UIViewController {
var delegate: Processor?
func someWork() {
if let processor = delegate {
processor.process(["Name" : "Pwd"])
} else {
print("Delegate not assigned")
}
}
}
Struggling to learn the basics of passing data via delegates. I am trying to pass a string from my viewController to my viewController2 and print it. I am getting the error:
"Type ViewController2 has no member delagate" in my view controller 2.
I cannot figure out where I have gone wrong.
viewController 1:
protocol datadelagate {
func printThisString(string: String)
}
class ViewController: UIViewController {
var delegate: datadelagate?
override func viewDidLoad() {
delegate?.printThisString(string: "This was passed from first controller to second controller")
}
}
This is my viewController 2:
class ViewController2: UIViewController, datadelagate {
func printThisString(string: String) {
print(string)
}
override func viewDidLoad() {
super.viewDidLoad()
ViewController2.delagate = self
print(String.self)
}
}
If you want ViewController2 to print some value from ViewController, you might have to do it this way:
protocol datadelagate {
func printThisString(string: String)
func getStringFromVC1() -> String
}
class ViewController: UIViewController, datadelagate {
let someString: String = "From VC1"
func printThisString(string: String) {
print(string)
}
func getStringFromVC1() -> String {
return someString
}
override func viewDidLoad() {
ViewController2.delagate = self
}
}
class ViewController2: UIViewController {
var delegate: datadelagate?
override func viewDidLoad() {
super.viewDidLoad()
//This is how something from VC2 is sent to VC1's scope.
delegate?.printThisString(string: "Calling the delegate to print something from ViewController2 on first ViewController")
//The below call gets you some value from VC1. (This is what you wanted, I belive...)
print(delegate?.getStringFromVC1())
}
}
Now for some explanation:
For simple understanding, assume a delegate as a person who does some specific job (protocol).
You have a `delegate'
You ask your delegate to work with your friend, and your friend acknowledges. (assigns your delegate by You.delegate = self, where self is your friend)
Now, through your delegate, you can do something with your friend, by asking your delegate to do some job (defined in protocol).
EDIT
The code above won't work, as non-static data members are trying to be accessed without creating an instance
Working code
import UIKit
class ViewController2: UIViewController {
static let sharedInstance = ViewController2()
weak var delegate: DataDelagate?
override func viewDidLoad() {
super.viewDidLoad()
//This is how something from VC2 is sent to VC1's scope.
delegate?.printThis(string: "Calling the delegate to print something from ViewController2 on first ViewController")
//The below call gets you some value from VC1. (This is what you wanted, I belive...)
print(delegate?.getStringFromVC1() ?? "s")
}
}
class ViewController: UIViewController {
static let sharedInstance = ViewController2()
var someString: String = "From VC1"
override func viewDidLoad() {
super.viewDidLoad()
ViewController2.sharedInstance.delegate = self
}
}
extension ViewController: DataDelagate {
func printThis(string: String) {
print(string)
}
func getStringFromVC1() -> String {
return someString
}
}
protocol DataDelagate: AnyObject {
func printThis(string: String)
func getStringFromVC1() -> String
}
In my project I have implemented a protocol which makes some URL call and return result, and my intent is to show result in UILabel. Following is my code :
protocol RestAPIResult
{
func retriveDriverInfo()
}
class RestAPICall : RestAPIResult
{
func retriveDriverInfo()
{
self.dashBoardViewController.getDriverInfo(driverProfile)
// calling another function of Next View for Lable Setup
}
}
getDriverInfo is in NextView which has Outlet of textVIew
class DashBoardViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
restDeledate = RestAPICall()
restDeledate!.retriveDriverInfo()
// IF LABEL SET HERE NO ERROR
//totalTripLabel.text = "Testing" // NO ERROR
}
func getDriverInfo(driverInfoArray : NSArray)
{
totalTripLabel.text = "Testing" // HERE IS ERROR
}
}
If Text is set in ViewDidLoad() it doesn't crash. but when i tried to set value in delegate function it crash saying found null.
protocol RestAPIResult: NSObjectProtocol
{
func retriveDriverInfo(message: String)
}
class RestAPICall : RestAPIResult
{
override func viewDidLoad()
{
super.viewDidLoad()
//create an instance of your vc and
instance.delegate = self
//if you code for push vc then write this line there insatance.delegate = self
}
func retriveDriverInfo(message: String)
{
yourLbaelOutlet.text = message
}
}
class DashBoardViewController: UIViewController
{
weak var delegate: RestAPIResult!
override func viewDidLoad()
{
super.viewDidLoad()
}
func getDriverInfo(driverInfoArray : NSArray)
{
self.delegate.retriveDriverInfo(message: "youMessage")
}
}
*
I try to access some IBOutlet outside of Viewdidload and IBAction, and always get nil value. In Viewdidload and IBAction, those value are ok. Did i miss a part to declare or initialize something ?
The value are modified after viewdidload() because viewdidload is called bu the IBAction.
The View is created in storyboard, coming from a UINavigation Controller.
connection table between ViewController and UIView:
The loginServer method is called by userCredential delegate, as below:
protocol userCredentialDelegate {
func didUpdateCredential (sender:String, credential: Bool?)
}
class userCredential: NSObject {
var delegate:userCredentialDelegate?
// self.delegate = ViewController() removed
func loginServer (name: String, pwd: String) -> Bool {
dispatch_sync(dispatch_get_main_queue())
{
self.delegate?.didUpdateCredential ("login", credential: credentialStatus)
}
}
Main controller:
class ViewController: UIViewController, userCredentialDelegate {
// set the shared instance
let user = userCredential.sharedInstance
#IBOutlet weak var incorrectCredentials: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var logButton: UIButton!
#IBAction func logButton(sender: UIButton) {
print (incorrectCredentials?.hidden)
if logButton.titleLabel!.text == "Log Out" {
user.logoutServer ()
} else {
user.loginServer(username.text!, pwd: password.text!)
}
}
func didUpdateCredential (sender: String, credential: Bool?) {
switch sender {
case "login":
if credential! {
performSegueWithIdentifier("loginSegue", sender: self)
} else {
incorrectCredentials?.hidden = false
}
default: break
}
if let credentialResponse = credential {
loginStatus = credentialResponse
}
}
var loginStatus: Bool = false {
didSet {
if loginStatus {
incorrectCredentials?.hidden = true // always nil before, now ok
} else {
incorrectCredentials?.hidden = false // always nil before, now ok
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
user.delegate = self
incorrectCredentials.hidden = true // can work here
user.getUserInfo ()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
connection table:
You need to set the delegate to your user model in your viewDidLoad function.
Before doing user.getUserInfo() make user.delegate = self
currently you create a new Instance on the user model, that has nothing todo with you real loaded ViewController.