I have two view controllers one with the 3 tableviews and other controller I have a uitextfield and the text entered in the text field I want to add it to one of the tableview called ScheduleTableView in the other view controller.
Here is my code but I get the error unexpectedly found nil while unwrapping an optional value at vc.ScheduleTableView.beginUpdates()
#IBAction func addButtonTapped(_ sender: YTRoundedButton) {
self.performSegue(withIdentifier: "secondvc", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "secondvc" {
print(TitleTextField.text!)
let vc = segue.destination as! GrowthMainViewController
vc.ScheduleArray.append(TitleTextField.text!)
let indexPath = IndexPath(row: vc.ScheduleArray.count - 1, section: 0)
vc.ScheduleTableView.beginUpdates()
vc.ScheduleTableView.insertRows(at: [indexPath], with: .automatic)
vc.ScheduleTableView.reloadData()
vc.ScheduleTableView.endUpdates()
TitleTextField.text = ""
view.endEditing(true)
}
}
The resolution for this was changing where the array was declared (singleton is fine for this), and removing the unnecessary inserting, updating, reloading, etc.
The numRowsInSection method then calls the scheduleArray.count to show all corresponding data.
Before:
#IBAction func addButtonTapped(_ sender: YTRoundedButton) {
self.performSegue(withIdentifier: "secondvc", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "secondvc" {
print(TitleTextField.text!)
let vc = segue.destination as! GrowthMainViewController
vc.ScheduleArray.append(TitleTextField.text!)
let indexPath = IndexPath(row: vc.ScheduleArray.count - 1, section: 0)
vc.ScheduleTableView.beginUpdates()
vc.ScheduleTableView.insertRows(at: [indexPath], with: .automatic)
vc.ScheduleTableView.reloadData()
vc.ScheduleTableView.endUpdates()
TitleTextField.text = ""
view.endEditing(true)
}
}
After :
#IBAction func addButtonTapped(_ sender: YTRoundedButton) {
self.performSegue(withIdentifier: "secondvc", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "secondvc" {
guard let text = TitleTextField.text else { return }
scheduleArray.append(text)
TitleTextField.text = ""
view.endEditing(true)
}
}
vc.ScheduleArray.count - 1 is probably a negative indexpath
try this
if (vc.ScheduleArray.count - 1 >= 0){
vc.ScheduleTableView.insertRows(at: [indexPath], with: .automatic)
}
The problem is that you trying to update a view controller inside of prepare function. Inside that function, the view is instantiated but it's Outlets are not connected yet.
To solve that issue, follow these steps:
On this method you should first update your model:
#IBAction func addButtonTapped(_ sender: YTRoundedButton) {
// update your model here
self.performSegue(withIdentifier: "secondvc", sender: self)
}
In your destination view controller, you should handle this model change and reload the table view's data.
override func viewDidLoad() {
self.tableView.reloadData()
}
Related
I'm very new to coding and I'm having trouble passing data to a second view controller. I want to change a label in the second view controller depending on which button is pressed in the first (if you press art the label says "art museum", if you press rollercoaster the label says "theme park")
I have tried putting the override function into the brackets for the button but I got the error "override can only be specified on class members"
firstacCode
class firstacCode: UIViewController {
#IBAction func art(_ sender: Any) {
performSegue(withIdentifier: "artSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = "art museum"
}
acCode
class acCode: UIViewController {
var descriptionLabel :String = "";
#IBOutlet weak var descriptionText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
descriptionText.text = descriptionLabel
// Do any additional setup after loading the view.
}
This worked for me for one button but I'm not sure how to add another button into the equation. Any help would be greatly appreciated.
First of all connect your buttons to the art function, from the attribute inspector give tags to the buttons.
Now your code will be look like this :
#IBAction func art(_ sender: UIButton) {
if sender.tag == 0{
performSegue(withIdentifier: "mySegue", sender: "art museum")
}
if sender.tag == 1{
performSegue(withIdentifier: "mySegue", sender: "Theme Park")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc : acCode = segue.destination as! acCode
vc.destVc = sender as! String
}
First off, welcome to Stackoverflow. You're already doing the passing of data to the other controller correctly. So your only problem now I guess is how to handle multiple buttons in your IBAction function.
Tip:
You can actually make this #IBAction func art(_ sender: Any) as #IBAction func art(_ sender: UIButton). Instead of Any, turn it into UIButton to avoid casting.
Assuming you connected multiple buttons into that art IBAction function, you can catch that specific button object through your sender and pass that sender to the sender parameter of your prepareForSegue, or in your case, I just realized that you might have different function for your two buttons, just pass the string to that sender parameter.
So your code would be like so:
#IBAction func art(_ sender: Any) {
performSegue(withIdentifier: "artSegue", sender: "art museum")
}
#IBAction func rollercoaster(_ sender: Any) {
performSegue(withIdentifier: "artSegue", sender: "theme park")
}
and in your prepareForSegue, cast the sender to String.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = sender as! String
}
#IBAction func art(_ sender: Any) {
sender.tag = 1
performSegue(withIdentifier: "artSegue", sender: self)
}
#IBAction func art2(_ sender: Any) {
sender.tag = 2
performSegue(withIdentifier: "art2Segue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(sender.tag == 1){
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = "art museum"
}
if(sender.tag == 2){
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = "art2 museum"
}
}
This is my code. I'm trying to perform a segue. I have a bar button item as well as a table with rows performing the same segue. I want to know when the button is clicked and when a particular row.
The following code works for a button but not for the rows of the table
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
activePlaceRow = indexPath.row
performSegue(withIdentifier: "toMap", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMap"
{
let mapViewController = segue.destination as! ViewController
if let senderObject = sender
{
if (senderObject as! UIBarButtonItem).tag == 1
{
mapViewController.activePlaceRow = -2
}
else
{
mapViewController.activePlaceRow = activePlaceRow
}
}
}
}
Just pass the index path as sender
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "toMap", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMap"
{
let mapViewController = segue.destination as! ViewController
if let buttonItem = sender as? UIBarButtonItem, buttomItem.tag == 1 {
mapViewController.activePlaceRow = -2
}
else if let indexPath = sender as? IndexPath {
mapViewController.activePlaceRow = indexPath.row
}
}
}
What you can do is add a property to your UIViewController
var isSegueFromCell:Bool = false
In your didSelectRowAt method you can assign this value as true. Then, when your prepareForSegue method gets called, you check:
if isSegueFromCell {
print("SEGUE FROM TABLE VIEW")
}
else {
print("SEGUE FROM BUTTON")
}
OR
When you call performSegue you can pass in a Bool indicating true if being called from your didSelectRow method
performSegue(withIdentifier: "YourSegueID", sender: true)
Then detect this in your prepareForSegue method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let value = sender as? Bool {
if value {
print("FROM TABLE VIEW ROW")
} else {
print("FROM BUTTON")
}
}
}
One easy thing to do is to check indexPathForSelectedRow or indexPathsForSelectedRows.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMap"
{
let mapViewController = segue.destination as! ViewController
if let senderObject = sender
{
if (senderObject as! UIBarButtonItem).tag == 1
{
mapViewController.activePlaceRow = -2
}
else
{
mapViewController.activePlaceRow = tableView.indexPathForSelectedRow
}
}
}
No need for the activePlaceRow this way. You can also create a segue in the storyboard by right clicking on the cell and dragging to the view controller, and then delete your didSelectRow implementation.
The question is a little confusing to articulate so hopefully an image will be a little more helpful. Here's the setup:
So, I want the First View button to open the FirstViewController, and I want the Second View button to open the SecondViewController. If I link the Second View button to the SecondViewController like this:
I lose the tab navigation. And if I connect the button to the TabViewController like this:
then it will automatically open into the FirstViewController. Unless I'm missing something, it seems like this would need to be done with a little extra code but I cannot seem to find anything that explains how to do this.
Thank you!
Create storyboard segue from Viewcontroller to TabBarController with an identifier. Then assign selelctedIndex value of TabBarController in prepareforsegue method
#IBAction func firstBtnAction(_ sender: Any) {
(sender as! UIButton).tag = 0
performSegue(withIdentifier: "tabBar", sender: sender)
}
#IBAction func secBtnAction(_ sender: Any) {
(sender as! UIButton).tag = 1
performSegue(withIdentifier: "tabBar", sender: sender)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "tabBar" {
if let vc = segue.destination as? UITabBarController {
vc.selectedIndex = (sender as! UIButton).tag
}
}
}
In tabViewController you need to implement following functions..
var index : Int!
#IBAction func button1(_ sender: Any) {
index = 1
self.performSegue(withIdentifier: "TabBarSegue", sender: self)
}
#IBAction func button2(_ sender: Any) {
index = 2
self.performSegue(withIdentifier: "TabBarSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "TabBarSegue"){
let videoController : TabBarController = segue.destination as! TabBarController
videoController.index = index
}
Then you need to implement TabBarController and implement following..
import UIKit
class TabBarController: UITabBarController {
var index : Int!
override func viewDidLoad() {
super.viewDidLoad()
selectedIndex = index
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I have a ViewController, "ViewController".
Within this view there is an attribute 'userEmailText.text' that I want to pass along to another view controller, "CreateNewCommunity"
I have a button connected to a segue with identifier 'createCommunitySegue'. I have create the code for when this is tapped:
#IBAction func createCommunityTapped(_ sender: AnyObject) {
self.performSegue(withIdentifier: "createCommunitySegue", sender: self)
}
In my second view controller, "CreateNewCommunity" I have a variable:
var myEmail: String?
and I have started to build the code ready to receive the data from the segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "createCommunitySegue" {
let createCommunityController: CreateNewCommunity = segue.destination as! CreateNewCommunity
createCommunityController.myEmail =
}
}
How do I finish this set-up so that the initial 'userEmailText.text' value is passed by "ViewController" through segue 'createCommunitySegue' into "CreateNewCommunity" view controller?
Thanks
Why not just
createCommunityController.myEmail = userEmailText.text
I think your problem is that you have a wrong idea of how to pass data from one vc to another.
The VC that you are passing data to doesn't recieve the data. It is the VC that has the data proactively pass the data to the VC that needs it.
My guess would be that you put this code
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "createCommunitySegue" {
let createCommunityController: CreateNewCommunity = segue.destination as! CreateNewCommunity
createCommunityController.myEmail =
}
}
in the CreateNewCommunity controller! You should instead put the above in ViewController not CreateNewCommunity , and write
createCommunityController.myEmail = userEmailText.text
it should work.
VC1
#IBAction func createCommunityTapped(_ sender: AnyObject) {
self.performSegue(withIdentifier: "createCommunitySegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "createCommunitySegue" {
let createCommunityController: CreateNewCommunity = segue.destination as! CreateNewCommunity
createCommunityController.myEmail = userEmailText.text
}
}
VC2
var myEmail: String?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(myEmail)
}
I'm facing the error message:
"UIStoryboardSegue does not have a member named 'identifier'"
Here's the code causing the error
if (segue.identifier == "Load View") {
// pass data to next view
}
On Obj-C it's fine using like this:
if ([segue.identifier isEqualToString:#"Load View"]) {
// pass data to next view
}
What am I doing wrong?
This seems to be due to a problem in the UITableViewController subclass template. It comes with a version of the prepareForSegue method that would require you to unwrap the segue.
Replace your current prepareForSegue function with:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "Load View") {
// pass data to next view
}
}
This version implicitly unwraps the parameters, so you should be fine.
Swift 4, Swift 3
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MySegueId" {
if let nextViewController = segue.destination as? NextViewController {
nextViewController.valueOfxyz = "XYZ" //Or pass any values
nextViewController.valueOf123 = 123
}
}
}
I think the problem is you have to use the ! to unbundle identifier
I have
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if segue!.identifier == "Details" {
let viewController:ViewController = segue!.destinationViewController as ViewController
let indexPath = self.tableView.indexPathForSelectedRow()
viewController.pinCode = self.exams[indexPath.row]
}
}
My understanding is that without the ! you just get a true or false value
For Swift 2.3,swift3,and swift4:
Create a perform Segue at didSelectRowAtindexPath
For Ex:
self.performSegue(withIdentifier: "uiView", sender: self)
After that Create a prepareforSegue function to catch the Destination segue and pass the value:
Ex:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "uiView"{
let destView = segue.destination as! WebViewController
let indexpath = self.newsTableView.indexPathForSelectedRow
let indexurl = tableDatalist[(indexpath?.row)!].link
destView.UrlRec = indexurl
//let url =
}
}
You need to create a variable named UrlRec in Destination ViewController
Swift 1.2
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "ShowDeal") {
if let viewController: DealLandingViewController = segue.destinationViewController as? DealLandingViewController {
viewController.dealEntry = deal
}
}
}
Prepare for Segue in Swift 4.2 and Swift 5.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "OrderVC") {
// pass data to next view
let viewController = segue.destination as? MyOrderDetailsVC
viewController!.OrderData = self.MyorderArray[selectedIndex]
}
}
How to Call segue On specific Event(Like Button Click etc):
performSegue(withIdentifier: "OrderVC", sender: self)
this is one of the ways you can use this function, it is when you want access a variable of another class and change the output based on that variable.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let something = segue.destination as! someViewController
something.aVariable = anotherVariable
}
Provided you aren't using the same destination view controller with different identifiers, the code can be more concise than the other solutions (and avoids the as! in some of the other answers):
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
if let myViewController = segue.destinationController as? MyViewController {
// Set up the VC
}
}
Change the segue identifier in the right panel in the section with an id. icon to match the string you used in your conditional.
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if(segue!.identifier){
var name = segue!.identifier;
if (name.compare("Load View") == 0){
}
}
}
You can't compare the the identifier with == you have to use the compare() method