I'm trying to pass text to TextView from other ViewController:
if (segue.identifier == "HomeToDetails") {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
nextVC.infoTextView.text = "TESTING"
}
But it crashes:
fatal error: unexpectedly found nil while unwrapping an Optional value
setting text on UITextField of DestinationViewController is not possible in the prepareForSegue:sender, because all view components of the recently allocated controller are not initialized before the view is loaded (at this time, they are all nil), they only will be when the DestinationViewController view is loaded.
You need to use optional variable infoString in DetailsViewController which you can set in prepareForSegue method
if (segue.identifier == "HomeToDetails") {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
nextVC.infoString = "TESTING"
}
in DetailsViewController.swift
class DetailViewController: UIViewController {
var infoString: String?
override func viewDidLoad() {
super.viewDidLoad()
if let info = infoString {
self.infoTextView.text = info
}
}
}
if value is nil and you unwrap it, then the app will crash, you should use optional binding to avoid crash.
if (segue.identifier == "HomeToDetails") {
if let nav = segue.destination as? UINavigationController { //should use optional binding to avoid crash
if let nextVC = nav.topViewController as? DetailsViewController {
nextVC.infoTextView.text = "TESTING"
}
}
}
Check this:
if (segue.identifier == "HomeToDetails") {
// get a reference to the second view controller
let secondViewController = segue.destination as! DetailsViewController
// set a variable in the second view controller with the String to pass
secondViewController.infoTextView.text = "TESTING"
}
Try this code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let nav = segue.destination as? UINavigationController {
if let nextVC = nav.topViewController as? DetailsViewController {
nextVC.infoTextView.text = "Data you want to pass"
}
}
}
this is because when you are assigning value to text view, the text view not initialized that time as viewDidLoad of next class is not called yet. So pass only string, then in viewDidLoad of next class, set the text.
You cannot pass a text directly to TextView in another view before it's created.
First ViewController:
if (segue.identifier == "HomeToDetails") {
let secondViewController = segue.destination as! DetailsViewController
secondViewController.myText = "TESTING"
}
DetailsViewController:
var myText = ""
override func viewDidLoad() {
super.viewDidLoad()
infoTextView.text = myText
}
All view components of the DetailsViewController are not initialized before the view is loaded. You can load that view before assigning values to the components of that view
Consider vC is the view controller which contains the view that you want to load. Then
if let _ = vC.view {
//Assign value here
}
Once the view is loaded, we can set the value to the components in that view. In SWIFT 3
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "HomeToDetails" {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
if let _ = nextVC.view {
nextVC.infoTextView.text = "TESTING"
}
}
}
Related
I'm following this tutorial link
But with one change that I made since the beginning.
I Added a ViewController (and made it the Initial View Controller) and Added a Container View in it.
Then I embed seagued between my View Container and the tutorial's SplitViewController.
And all worked well, until I got to this step:
Go to to AppDelegate.swift and replace the implementation of application(_:didFinishLaunchingWithOptions:) with the following:
guard let splitViewController = window?.rootViewController as? UISplitViewController,
let leftNavController = splitViewController.viewControllers.first as? UINavigationController,
let masterViewController = leftNavController.topViewController as? MasterViewController,
let detailViewController = splitViewController.viewControllers.last as? DetailViewController
else { fatalError() }
let firstMonster = masterViewController.monsters.first
detailViewController.monster = firstMonster
return true
How this code should be modified to work with my case?
In your case you can't get access to uisplitviewcontroller in AppDelegate. Since you are using Container Embedded view like in image below. You get reference of the UISplitviewcontroller from prepareFor segue of your first viewController. So instead of writing code in AppDelegate, try do that in prepare for segue in your initial viewController.
Initial ViewController
class ViewController: UIViewController {
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "embedseg" {
guard let splitViewController = segue.destination as? UISplitViewController,
let leftNavController = splitViewController.viewControllers.first as? UINavigationController,
let masterViewController = leftNavController.topViewController as? MasterViewController,
let rightNavController = splitViewController.viewControllers.last as? UINavigationController,
let detailViewController = rightNavController.topViewController as? DetailViewController
else { fatalError() }
let firstMonster = masterViewController.monsters.first
detailViewController.monster = firstMonster
masterViewController.delegate = detailViewController
detailViewController.navigationItem.leftItemsSupplementBackButton = true
detailViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
}
}
}
Have a Table View Controller that will pass an ID field onto a View Controller in order to retrieve detail however in between the two controllers is a Tab Bar Controller. I am unsure how I am to get the information passed between the two. Was attempting to use a Segue but the value is blank once it gets to the detail controller.
EventBarTableViewCell.swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showEventDetail" {
if let IndexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as? eventDetailViewController
controller?.inTradeShowID = (events?[IndexPath.row].tradeshowID!)!
controller?.viaSegue = (events?[IndexPath.row].tradeshowID!)!
//controller?.performSegue(withIdentifier: "tradeShowID", sender: self)
//if shouldShowSearchResults {
// controller?.viaSegue = filteredArray[IndexPath.row].charterNum!
//} else {
// controller?.viaSegue = repositories[IndexPath.row].charterNum!
//}
}
}
}
eventDetailViewController.swift
var viaSegue = ""
var inTradeShowID = ""
override func viewDidLoad() {
super.viewDidLoad()
inTradeShowID = self.viaSegue
}
Could use some help.
You are segueing to a Tab Bar Controller, not to your "display" view, so you need to "drill down" so to speak:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showEventDetail" {
if let IndexPath = self.tableView.indexPathForSelectedRow {
// we're segueing to a Tab Bar Controller
if let tabBarVC = segue.destination as? UITabBarController {
// get the first view controller of the Tab Bar Controller
// *that* is where you want to "pass" your data
if let controller = tabBarVC.viewControllers?.first as? eventDetailViewController {
// either should work
controller.inTradeShowID = (events?[IndexPath.row].tradeshowID!)!
controller.viaSegue = (events?[IndexPath.row].tradeshowID!)!
} //end if let controller = tabBarVC.viewControllers?.first as? eventDetailViewController
} //end if let tabBarVC = segue.destination as? UITabBarController
} //end if let IndexPath = self.tableView.indexPathForSelectedRow
} //end if segue.identifier == "showEventDetail"
}
My view controllers are sitting inside of view containers.
I'm trying to change the nav controller in the green area with the buttons from the purple area.
In my Main View controller I have:
class MainViewController: UIViewController,Purpleprotocol,Greenprotocol {
weak var infoNav : UINavigationController?
weak var greenVC: GreenVC?
weak var purpleVC: PurpleVC?
weak var peachVC: PeachVC?
func changenav(whichbutton: String) {
print ("changenav")
print(whichbutton + "whichbuttonstring")
if(whichbutton == "1"){
print("change one")
let svc = storyboard?.instantiateViewController(withIdentifier: "rootController") as! GreenVC
self.infoNav?.pushViewController(svc, animated: true)
}
if(whichbutton == "2"){
print("change two")
let svc = storyboard?.instantiateViewController(withIdentifier: "secondController") as! SecondViewController
self.infoNav?.pushViewController(svc, animated: true)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "contentSegue" {
print("contentSegue")
let infoNav = segue.destination as! UINavigationController
let greenVC = infoNav.viewControllers[0] as! GreenVC
greenVC.delegate = self
}
if segue.identifier == "menuSegue" {
let dvmenu = segue.destination as! PurpleVC
purpleVC = dvmenu
dvmenu.delegate = self
}
}
This function does work and I can see the "change one" etc being called but the navigation controller is just not changing..not sure how to get to it I guess correctly.
#IBOutlet weak var menuButton: UIButton!
#IBOutlet weak var clubButton: UIButton!
#IBOutlet weak var announcemnetsButton: UIButton!
#IBOutlet weak var eventButton: UIButton!
let transition = CircularTransition()
override func viewDidLoad() {
super.viewDidLoad()
menuButton.layer.cornerRadius = menuButton.frame.size.width / 2
clubButton.layer.cornerRadius = menuButton.frame.size.width / 2
announcemnetsButton.layer.cornerRadius = menuButton.frame.size.width / 2
eventButton.layer.cornerRadius = menuButton.frame.size.width / 2
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! NewViewController
secondVC.transitioningDelegate = self
secondVC.modalPresentationStyle = .custom
let thirdVC = segue.destination as! ClubsViewController
thirdVC.transitioningDelegate = self
thirdVC.modalPresentationStyle = .custom
let fourthVC = segue.destination as! AnnouncementsViewController
fourthVC.transitioningDelegate = self
fourthVC.modalPresentationStyle = .custom
let fifthVC = segue.destination as! EventsViewController
fifthVC.transitioningDelegate = self
fifthVC.modalPresentationStyle = .custom
}
I am running this code but I keep getting the error, what am I doing wrong? I believe everything is linked correctly, but I keep getting the SIGABRT error.
Every Segue has a identifier. You have to set identifier for segue
In the prepare you are not checking the segue identifier. Due to that you are trying to forcefully convert the segue.destination controller four different controller, which is wrong.
Please see how to set segue identifier
And change your code based on your segue identifier
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "NewVC" {
let secondVC = segue.destination as! NewViewController
secondVC.transitioningDelegate = self
secondVC.modalPresentationStyle = .custom
} else if segue.identifier == "ClubVC" {
let thirdVC = segue.destination as! ClubsViewController
thirdVC.transitioningDelegate = self
thirdVC.modalPresentationStyle = .custom
} else if segue.identifier == "AnnouncementVC" {
let fourthVC = segue.destination as! AnnouncementsViewController
fourthVC.transitioningDelegate = self
fourthVC.modalPresentationStyle = .custom
} else if segue.identifier == "EventVC" {
let fifthVC = segue.destination as! EventsViewController
fifthVC.transitioningDelegate = self
fifthVC.modalPresentationStyle = .custom
}
}
You seem to be converting the same segue destination to many different types of VC which it obviously cannot be all at the same time.
I think what you intended to do is to check if the segue destination is of the specific type (in which case you must not force-unwrap with !):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? NewViewController {
secondVC.transitioningDelegate = self
secondVC.modalPresentationStyle = .custom
} else if let thirdVC = segue.destination as? ClubsViewController {
thirdVC.transitioningDelegate = self
thirdVC.modalPresentationStyle = .custom
} else if let fourthVC = segue.destination as? AnnouncementsViewController {
fourthVC.transitioningDelegate = self
fourthVC.modalPresentationStyle = .custom
} else if let fifthVC = segue.destination as? EventsViewController {
fifthVC.transitioningDelegate = self
fifthVC.modalPresentationStyle = .custom
}
}
Using segue identifiers though could be more sensible in this situation.
Then again, considering that you are always performing the same actions no matter what VC that is, maybe it's worth casting it to a generic VC (i.e. as! UIViewController and performing an action on that instead of differentiating them?
I am trying to create a simple contact list app as an avenue of learning Swift. Unfortunately no matter what I try I cannot have my contacts' details display into an editable text field on a different view controller to the tableview (which contains the main contacts).
Here is the block of code that should be effecting the text fields (i am unsure of what other code to supply)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
print(object.firstName)
controller.fiName?.text = object.firstName
controller.laName?.text = object.lastName
controller.miName?.text = object.middleName
controller.aess?.text = object.address
controller.phoNumber?.text = object.phoneNumber
controller.yearBirth?.text = String(object.age)
print(controller.fiName)
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
The result of the first print line is as I expect it to be, which is the name of the contact, but the program crashes on the second print, with an 'EXC_BAD_INSTRUCTION' / fatal error: unexpectedly found nil while unwrapping and optional value.
Why can I not set the controller.fiName?.text? Thank you in advance for any help.
I guess your filName is an UILabel or UITextField right?
If you check your code in DetailViewController you will notice this line:
#IBOutlet weak var filName: UITextField!
You see the ! at the end of the line?
This means, that the variable (aka outlet) filName cannot be set at the initialization, but will be set at some point later.
Now, what you do is you init your class, and try to access that property that is not yet set at the initialization.
To get around this, create an object instance in DetailViewController, and set that instance to the selected object, and then set the label properties in the viewDidLoad function.
Something like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.object = object
}
}
And then in the viewDidLoad of DetailViewController:
override viewDidLoad() {
fiName?.text = object.firstName
laName?.text = object.lastName
miName?.text = object.middleName
aess?.text = object.address
phoNumber?.text = object.phoneNumber
yearBirth?.text = String(object.age)
navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
navigationItem.leftItemsSupplementBackButton = true
}
You are setting the fiName label text too early, where the label hasn't been allocated yet. Instead pass through the object and then set the text in viewDidLoad of the next controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
//pass through object:
controller.object = object
print(object)
}
}
And then in the next viewController viewDidLoad set the text:
override viewDidLoad() {
//Now set the text value here:
fiName?.text = object.firstName
}
Just object should be passed in prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
And then outlets should be setup in viewDidLoad
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
fiName?.text = detailItem.firstName
laName?.text = detailItem.lastName
miName?.text = detailItem.middleName
aess?.text = detailItem.address
phoNumber?.text = detailItem.phoneNumber
yearBirth?.text = String(detailItem.age)
}
}