In ViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "Segue") {
var svc = segue.destinationViewController as! ViewController2;
svc.vericik = self.vericik
}
}
#IBAction func gotoView2(sender: AnyObject) {
self.performSegueWithIdentifier("Segue", sender: self)
self.presentViewController(ViewController2(), animated: true, completion: nil)
}
In ViewController2:
var vericik: String!
#IBOutlet weak var VeriYeri: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
VeriYeri.text = vericik
}
When, I click button on ViewController, ViewController2 page comes to screen and I can see segue data which come from ViewController. But after that, an error occurs:
fatal error: unexpectedly found nil while unwrapping an Optional value
Where am I doing wrong?
Your problem is this line:
self.presentViewController(ViewController2(), animated: true, completion: nil)
This line of code is creating a second ViewController2. The first one was created for you when you did self.performSegueWithIdentifier("Segue", sender: self). This second ViewController2 never gets initialized, so its vericik property is still nil when viewDidLoad runs and implicitly unwraps the optional with VeriYeri.text = vericik (because vericik is declared as String!).
To fix the problem, simply delete this line of code:
self.presentViewController(ViewController2(), animated: true, completion: nil)
It is not needed. The segue creates ViewController2 for you, and you initialize it in prepareForSegue, and then the segue presents ViewController2. There's absolutely no need for you to call presentViewController when using segues.
Somehow, you are setting your vericik variable to nil. Can you print that value before you try setting it before the segue? If it's nil, don't set it as the text, because that's causing the crash.
You need to check that your string is not nil. Try this.
ViewController2
override func viewDidLoad() {
super.viewDidLoad()
if vericik != nil {
//String is not nil, set textfield/label whatever
VeriYeri.text = vericik
}
}
ViewController
if (segue.identifier == "Segue") {
var svc = segue.destinationViewController as! ViewController2;
if self.vericik != nil {
//String is not nil. All is good :)
svc.vericik = self.vericik
} else {
//String is nil, do something...maybe set default text
svc.vericik = "some text"
}
}
Related
I am a beginner in iOS.
I have a problem that I made a class named ModalViewController. To make this as a common class, I didn't give any text to the textView in this class. So I tried to change the text when I present this viewController.
But I am getting an error. How can I fix it?
In addition, how can I change the margin value in text view?
Main.storyboard
MainViewController.swift
import UIKit
var modalview : ModalViewController!
class ViewController: UIViewController {
...
#IBAction func Onclick(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController")
myAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
modalview.modalCustomAlert.text = "test test test text" // Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
self.present(myAlert, animated: true, completion: nil)
}
ModalViewController.swift
import Foundation
import UIKit
class ModalViewController : UIViewController {
#IBOutlet weak var modalCustomAlert: UITextView!
#IBOutlet weak var cancelButton: UIButton!
#IBOutlet weak var okButton: UIButton!
#IBAction func cancelPress(_ sender: Any) {
}
#IBAction func okPress(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
modalCustomAlert.layer.cornerRadius = 8
}
#IBAction func modalbutton(_ sender: Any) {
}
}
Edited start
I tried to assign a value but there was no change in the error.
var modalview = ModalViewController()
...
#IBAction func Onclick(_ sender: Any) {
...
modalview.modalCustomAlert.text = "test test test text" // Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
}
Edited TWO start
I've tried this already. But when I saw the answer from #PGDev, I tried it again, but it showed the same error.
#IBAction func Onclick(_ sender: Any) {
...
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController") as! ModalViewController
myAlert.modalPresentationStyle = .overCurrentContext
myAlert.modalTransitionStyle = .crossDissolve
myAlert.modalCustomAlert.text = "test test test text" // Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
self.present(myAlert, animated: true, completion: nil)
}
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
modalview.modalCustomAlert.text = "test test test text"
The exception is because either modalview is nil or modalCustomAlert is nil.
1. You've created modalview as implicitly unwrapped optional like,
var modalview : ModalViewController!
Check if you've assigned any value to modalview before accessing it. If you haven't this will result in crash.
2. Next, you have created an outlet of modalCustomAlert,
#IBOutlet weak var modalCustomAlert: UITextView!
This is also an implicitly unwrapped optional. Check if the outlet is connected properly. If it isn't it will raise the runtime exception.
If an implicitly unwrapped optional is nil and you try to access its
wrapped value, you’ll trigger a runtime error. The result is exactly
the same as if you place an exclamation mark after a normal optional
that doesn’t contain a value.
Edit-1:
var modalview = ModalViewController()
The above code won't connect the outlets in ModalViewController. That's the reason modalCustomAlert is nil and result in exception.
Create modalview using storyboard like,
let modalview = storyboard.instantiateViewController(withIdentifier: "ModalViewController")
One question though. What's the reason to create modalview and myAlert separately if they both are of type ModalViewController?
Edit-2:
I don't think there is even a need to create modalview. Simply use myAlert view at the line where you're getting the exception. So the code for ViewController goes like,
class ViewController: UIViewController {
//....
#IBAction func Onclick(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController") as! ModalViewController
myAlert.modalPresentationStyle = .overCurrentContext
myAlert.modalTransitionStyle = .crossDissolve
myAlert.modalCustomAlert.text = "test test test text"
self.present(myAlert, animated: true, completion: nil)
}
}
Edit-3:
Got that. Just didn't realise that you're accessing modalCustomAlert before the outlets are created.
First of all, in ModalViewController create a property text. And in viewDidLoad() add this text as modalCustomAlert.text, i.e.
class ModalViewController : UIViewController {
//rest of the code....
var text: String? //here....
override func viewDidLoad() {
super.viewDidLoad()
modalCustomAlert.layer.cornerRadius = 8
self.modalCustomAlert.text = text //here....
}
}
Now, when creating alertView, replace
myAlert.modalCustomAlert.text = "test test test text"
with
myAlert.text = "test test test text"
Please add one new variable String in Your ModalViewController
class ModalViewController : UIViewController {
var objTextViewString:String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.modalCustomAlert.text = self.objTextViewString
}
}
On ModelViewController Present Please pass String On added String Variable..That's It..!!
class ViewController: UIViewController {
//....
#IBAction func Onclick(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController") as! ModalViewController
myAlert.modalPresentationStyle = .overCurrentContext
myAlert.modalTransitionStyle = .crossDissolve
myAlert.objTextViewString = "test test test test"
self.present(myAlert, animated: true, completion: nil)
}
}
Consider two view controller Controller1 and Controller2, I have created a form of many UITextField in controller 1, in that when a user clicks a particular UITextField it moves to Controller2 and he selects the data there.
After selecting the data in Controller2 it automatically moves to Controller1, while returning from controller2 to controller1 other UITextfield data got cleared and only the selected data from controller2 is found. I need all the data to be found in the UITextfield after selecting.
Here is the code for returning from Controller2 to Controller1
if(Constants.SelectedComplexName != nil)
{
let storyBoard: UIStoryboard = UIStoryboard(name: "NewUserLogin", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "NewUser") as! NewUserRegistrationViewController
self.present(newViewController, animated: true, completion: nil)
}
To pass messages you need to implement Delegate.
protocol SecondViewControllerDelegate: NSObjectProtocol {
func didUpdateData(controller: SecondViewController, data: YourDataModel)
}
//This is your Data Model and suppose it contain 'name', 'email', 'phoneNumber'
class YourDataModel: NSObject {
var name: String? //
var phoneNumber: String?
var email: String?
}
class FirstViewController: UIViewController, SecondViewControllerDelegate {
var data: YourDataModel?
var nameTextField: UITextField?
var phoneNumberTextField: UITextField?
var emailTextField: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
callWebApi()
}
func callWebApi() {
//After Success Fully Getting Data From Api
//Set this data to your global object and then call setDataToTextField()
//self.data = apiResponseData
self.setDataToTextField()
}
func setDataToTextField() {
self.nameTextField?.text = data?.name
self.phoneNumberTextField?.text = data?.phoneNumber
self.emailTextField?.text = data?.email
}
func openNextScreen() {
let vc2 = SecondViewController()//Or initialize it from storyboard.instantiate method
vc2.delegate = self//tell second vc to call didUpdateData of this class.
self.navigationController?.pushViewController(vc2, animated: true)
}
//This didUpdateData method will call automatically from second view controller when the data is change
func didUpdateData(controller: SecondViewController, data: YourDataModel) {
}
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
func setThisData(d: YourDataModel) {
self.navigationController?.popViewController(animated: true)
//Right After Going Back tell your previous screen that data is updated.
//To do this you need to call didUpdate method from the delegate object.
if let del = self.delegate {
del.didUpdateData(controller: self, data: d)
}
}
}
push your view controller instead of a present like this
if(Constants.SelectedComplexName != nil)
{
let storyBoard: UIStoryboard = UIStoryboard(name: "NewUserLogin", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "NewUser") as! NewUserRegistrationViewController
self.navigationController?.pushViewController(newViewController, animated: true)
}
and then pop after selecting your data from vc2 like this
self.navigationController?.popViewController(animated: true)
and if you are not using navigation controller then you can simply call Dismiss method
self.dismiss(animated: true) {
print("updaae your data")
}
There are a few ways to do it, but it usually depends on how you move from VC#1 to VC#2 and back.
(1) The code you posted implies you have a Storyboard with both view controllers. In this case create a segue from VC#1 to VC#2 and an "unwind" segue back. Both are fairly easy to do. The link provided in the comments does a good job of showing you, but, depending on (1) how much data you wish to pass back to VC#1 and (2) if you wish to execute a function on VC#2, you could also do this:
VC#1:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowVC2" {
if let vc = segue.destination as? VC2ViewController {
vc.VC1 = self
}
}
}
VC#2:
weak var VC1:VC1ViewController!
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isMovingFromParentViewController {
VC1.executeSomeFunction()
}
}
Basically you are passing the entire instance of VC1 and therefore have access to everything that isn't marked private.
(2) If you are presenting/dismissing VC#2 from VC#1, use the delegate style as described by one of the answers.
VC#1:
var VC2 = VC2ViewController()
extension VC1ViewController: VC2ControlllerDelegate {
func showVC2() {
VC2.delegate = self
VC2.someData = someData
present(VC2, animated: true, completion: nil)
}
function somethingChanged(sender: VC2ViewController) {
// you'll find your data in sender.someData, do what you need
}
}
VC#2:
protocol VC2Delegate {
func somethingChanged(sender: VC2ViewController) {
delegate.somethingChanged(sender: self)
}
}
class DefineViewController: UIViewController {
var delegate:DefineVCDelegate! = nil
var someData:Any!
func dismissMe() {
delegate.somethingChanged(sender: self)
dismiss(animated: true, completion: nil)
}
}
}
Basically, you are making VC#1 be a delegate to VC2. I prefer the declaration syntax in VC#2 for `delegate because if you forget to set VC#1 to be a delegate for VC#2, you test will force an error at runtime.
I am trying to pass some string data to a viewcontroller using performSegueWithIdentifier, but I get this error Cannot convert value of type 'AnyObject?'. Type(Aka'Optional<AnyObject>.Type) to expected argument type 'AnyObject?'
Even if I use sender:self, it still does not work.
In the storyboard, the segue is made by dragging a segue from 1st to 2nd view controller.
#IBAction func resetPassword(sender: AnyObject) {
FIRAuth.auth()?.sendPasswordResetWithEmail(emailTextField.text!, completion: { (error) in
var customError = error?.localizedDescription
if error == nil {
let noError = "Click on the link received in the email"
self.emailTextField.text = ""
self.emailTextField.attributedPlaceholder = NSAttributedString(string: noError, attributes:[NSForegroundColorAttributeName: UIColor.blueColor()])
self.customErroSent = noError
performSegueWithIdentifier("fromSeventhToFifth", sender: AnyObject?)
//self.resetButtonOutlet.hidden = true
// self.emailTextField.hidden = true
} else {
self.emailTextField.text = ""
self.emailTextField.attributedPlaceholder = NSAttributedString(string:customError!, attributes:[NSForegroundColorAttributeName: UIColor.redColor()])
}
})
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "fromSeventhToFifth" {
if let destViewController = segue.destinationViewController as? FifthViewController {
destViewController.label.text = customErroSent
}
}
}
}
The sender parameter is of type AnyObject? - so you can supply any object reference or nil, but you can't put AnyObject? since that is a type, not an object.
The error you are getting when you make this change, Implicit use of 'self' in closure, refers to the invocation of the function performSegueWithIdentifier, not the sender argument.
Since you are calling the function from within a closure, Swift needs to ensure that the closure captures self i.e. prevents it from being deallocated while the closure still exists.
Outside the closure this capture isn't necessary as if the object that self refers to has been deallocated the code can't be executing (The code is part of self).
To capture self, simply refer to it inside the closure:
self.performSegueWithIdentifier("fromSeventhToFifth", sender: self)
or
self.performSegueWithIdentifier("fromSeventhToFifth", sender: nil)
AnyObject? is a optional type. You should set it nil or any instance of Class. For example:
performSegueWithIdentifier("fromSeventhToFifth", sender: nil)
performSegueWithIdentifier("fromSeventhToFifth", sender: slef)
Swift 4.0, in TableView Project Template.
To declare:
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "fromSeventhToFifth" {
if let indexPath = tableView.indexPathForSelectedRow
{
}
}
}
To call:
performSegue(withIdentifier: "fromSeventhToFifth", sender: self)
I recently started to learn swift and it has been pretty good so far. Currently I'm having an issue trying to pass data between view controllers. I managed to figure out how to programmatically navigate between two view controllers using a navigation controller. Only problem is now I'm having a hard time trying to figure out how to pass three string entered by the user (for json api) to the next view.
Here's my current attempt. Any help is much appreciated!
ViewController:
/* Get the status code of the connection attempt */
func connection(connection:NSURLConnection, didReceiveResponse response: NSURLResponse){
let status = (response as! NSHTTPURLResponse).statusCode
//println("status code is \(status)")
if(status == 200){
var next = self.storyboard?.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController
self.presentViewController(next, animated: false, completion: nil)
}
else{
RKDropdownAlert.title("Error", message:"Please enter valid credentials.", backgroundColor:UIColor.redColor(), textColor:UIColor.whiteColor(), time:3)
drawErrorBorder(usernameField);
usernameField.text = "";
drawErrorBorder(passwordField);
passwordField.text = "";
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let navigationController = segue.destinationViewController as! UINavigationController
let newProjectVC = navigationController.topViewController as! SecondViewController
newProjectVC.ip = ipAddressField.text
newProjectVC.username = usernameField.text
newProjectVC.password = passwordField.text
}
SecondViewController:
import UIKit
class SecondViewController: UIViewController {
var ip:NSString!
var username:NSString!
var password:NSString!
override func viewDidLoad() {
super.viewDidLoad()
println("\(ip):\(username):\(password)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The method prepareForSegue is called when your app's storyboard performs a segue (a connection that you create in storyboards with Interface Builder). In the code above though you are presenting the controller yourself with presentViewController. In this case, prepareForSegue is not fired. You can do your setup right before presenting the controller:
let next = self.storyboard?.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController
next.ip = ipAddressField.text
next.username = usernameField.text
next.password = passwordField.text
self.presentViewController(next, animated: false, completion: nil)
You can read more about segue here
Updated syntax for Swift 3:
let next = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
next.ip = ipAddressField.text
next.username = usernameField.text
next.password = passwordField.text
self.present(next, animated: true, completion: nil)
I have two View Controllers: "DiscoverViewController" and "LocationRequestModalViewController".
The first time a user opens the "DiscoverViewController", I overlay "LocationRequestModalViewController" which contains a little blurb about accessing the users location data and how it can help them.
On the "LocationRequestModalViewController" there are two buttons: "No thanks" and "Use location". I need to send the response from the user back to the "DiscoverViewController"
I have done some research and found that delegates/protocols are the best way to do it, so I followed a guide to get that working, but I'm left with 2 errors and can't figure them out.
The errors are:
On DiscoverViewController
'DiscoverViewController' is not convertible to 'LocationRequestModalViewController'
On LocationRequestModalViewController
'LocationRequestModalViewController' does not have a member name 'sendBackUserLocationDataChoice'
I've marked where the errors are happen in the following files:
DiscoverViewController.swift
class DiscoverViewController: UIViewController, UITextFieldDelegate, CLLocationManagerDelegate, LocationRequestModalViewControllerDelegate {
func showLocationRequestModal() {
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var locationRequestVC: AnyObject! = storyboard.instantiateViewControllerWithIdentifier("locationRequestVC")
self.presentingViewController?.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
self.tabBarController?.presentViewController(locationRequestVC as UIViewController, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
}
func sendBackUserLocationDataChoice(controller: LocationRequestModalViewController, useData: Bool) {
var enableData = useData
controller.navigationController?.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
showLocationRequestModal()
}
}
LocationRequestModalViewController
protocol LocationRequestModalViewControllerDelegate {
func sendBackUserLocationDataChoice(controller:LocationRequestModalViewController,useData:Bool)
}
class LocationRequestModalViewController: UIViewController {
var delegate:LocationRequestModalViewController? = nil
#IBAction func dontUseLocationData(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func useLocationData(sender: AnyObject) {
delegate?.sendBackUserLocationDataChoice(self, useData: true) // This is where error #2 happens
}
override func viewDidLoad() {
super.viewDidLoad()
//Modal appearance stuff here...
}
}
The answer is in your question itself. Both errors tells the exact reason.
Issue 1
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
The self is of type DiscoverViewController
But you declared the delegate as:
var delegate:LocationRequestModalViewController? = nil
You need to change that to:
var delegate:DiscoverViewController? = nil
Issue 2
The same reason, LocationRequestModalViewController does not confirm to the LocationRequestModalViewControllerDelegate, change the delegate declaration.
You have defined your delegate as having type LocationRequestModalViewController which does not conform to LocationRequestModalViewControllerDelegate.