How to pass data from a tableview to a different uiviewcontroller - swift

I am writing an application that is going to contain a tableview that contains a list of days. When a day is clicked, I would like to display a page that contains information with text and a button that is unique to each day.
I was planning on creating a different view controller that would be specific to each day. However, I do not know how to pass the data from the tableview for each day to the specific view controller of the specific day selected.

You can use UITableView delegate method for click event in your tableview
You need to implement UITableViewDelegate. For passing data to specific view controller you may want to use prepareForSegue function
var day = [1,2,3,4,5]
var selected_day : Int = 0
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.day.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("yourcellidentifier") as! yourtableViewCell
cell.labelday.text = self.day[indexPath.row]// just sample
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//this method will be called when you click 1 of the row from tableview
self.selected_day = self.day[indexPath.row]
self.performSegueWithIdentifier("ToYourSpecificViewController", sender: self) // you have to link with your table view controller and your specific view controller with an identifier.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.destinationViewController is YourSpecificViewController{
let vc = segue.destinationViewController as! YourSpecificViewController
// In YourSpecificViewController, you also need to declare a variable name called selected_day to catch
vc.selected_day = self.selected_day
}
}
Hope this help!

In the view controller with the table, implement the prepareforsegue() method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let row = self.tableView.indexPathForSelectedRow?.row
if segue.identifier == "Sunday" {
let vc = segue.destinationViewController as! SundayViewController
vc.myInt = dataModel[row!].theInt // This changes depending on how your data is set up and whether you're grabbing the info from a text field, or what have you
}
else if segue.identifier == "Monday" {
let vc = segue.destinationViewController as! MondayViewController
vc.myInt = dataModel[row!].theInt
vc.someString = dataModel[row!].theString
}
}
The days' view controllers would look like:
class SundayViewController: UIViewController {
var myInt: Int?
// etc
}
class MondayViewController: UIViewController {
var myInt: Int?
var someString: String?
// etc
}

In your tableviewcontroller implement this code
class TableviewController: UITableViewController {
var array : [DayObject]? = [DayObject(day: "Sunday", daytext: "SundayText"),DayObject(day: "Monday", daytext: "MondayText"),DayObject(day: "tuesday", daytext: "TuesdayText"),DayObject(day: "Wednesday", daytext: "WednesdayText")]
var object: DayObject?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension TableviewController {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (array!.count)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
cell?.textLabel?.text = array![indexPath.row].day
return cell!
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
object = array![indexPath.row]
performSegueWithIdentifier("NVC", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "NVC" {
let dvc = segue.destinationViewController as? ViewController2
dvc!.object = object
}
}
}
and make a datamodel as below:
import UIKit
class DayObject: NSObject {
var day: String!
var daytext: String!
init(day: String, daytext: String) {
self.day = day
self.daytext = daytext
}
}
and in your view controller you can collect the object
class ViewController2: UIViewController {
var object: DayObject!
override func viewDidLoad() {
super.viewDidLoad()
print(object.daytext)
}
}
By datamodel approach you dont have to make different view controllers for each day
happycoding :-)

Related

Nothing Happens When UITableView is Clicked (trying to send data between views)

So I have a two page app. The purpose of the app being the user can store expenses. They log a name and amount (attributes) and this data is stored in Expenses (entity). I have figured out how to create core data values, delete and retrieve. I am now working on updating. This will work by the user tapping on a table in the first view (ExpensesViewController) where the expenses are stored and this takes them to the 2nd view (EditExpensesViewController) where they can update the value back into core data. I am stuck on this 'data transfer' between the views.
I am using the storyboard and connected the first view to the second via 'show' set the segue identifier as 'editExpense'. However nothing happens when the table row is tapped. Any idea why it's not working and what I may have missed out? See here for GIF
ExpensesViewController
import UIKit
import CoreData
class ExpensesViewController: UIViewController {
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
var expenses_array = [Expenses]()
var send_array = [Expenses]()
override func viewDidLoad(){
super.viewDidLoad()
retrieveExpenses()
}
func retrieveExpenses(){
let fetchRequest: NSFetchRequest<Expenses> = Expenses.fetchRequest()
do {
let expenses = try PersistenceService.context.fetch(fetchRequest)
self.expenses_array = expenses
self.tableView.reloadData()
} catch {
print(error.localizedDescription )
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "editExpense") {
let secondViewController = segue.destination as! EditExpensesViewController
secondViewController.send_array = send_array
}
}
}
extension ExpensesViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return expenses_array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
cell.textLabel?.text = expenses_array[indexPath.row].name
cell.detailTextLabel?.text = expenses_array[indexPath.row].amount
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
let fetchRequest: NSFetchRequest<Expenses> = Expenses.fetchRequest()
do {
let result = try PersistenceService.context.fetch(fetchRequest)
// Delete from Core Data and remove from the arrays then save
if result.contains(expenses_array[indexPath.row]){
PersistenceService.context.delete(expenses_array[indexPath.row])
expenses_array = expenses_array.filter { $0 != expenses_array[indexPath.row] }
PersistenceService.saveContext()
self.getTotalExpenses()
self.tableView.reloadData()
}
} catch {
print(error.localizedDescription )
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
send_array = [self.expenses_array[indexPath.row]]
self.performSegue(withIdentifier: "editExpense", sender: self)
}
}
EditExpensesViewController
import UIKit
import CoreData
class EditExpensesViewController: UIViewController {
var send_array = [Expenses]() // Defined from the previous view controller
override func viewDidLoad() {
super.viewDidLoad()
print(send_array)
}
}
First of all conform to tableView delegates and dataSource in your viewDidLoad() :
tableView.delegate = self
tableView.dataSource = self
Delete segue from stroyboard and we will present the controller in code using :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "editExpense") {
let secondViewController = segue.destination as! EditExpensesViewController
secondViewController.send_array = send_array
// "someIdentifier" is the identifier of secondController in storyboard
storyboard?.instantiateViewController(withIdentifier: "someIdentifier")
present(secondViewController, animated: true, completion: nil)
}
}
Be aware to put storyboard identifier for second controller in storyboard using attribute inspector
The problem is that your first view controller is the UITableViewDataSource only. That is not enough. It needs to be the UITableViewDelegate too. didSelectRowAt Is a delegate method, not a data source method, and will not be called unless this view controller is the table views delegate and is explicitly declared as conforming to UITableViewDelegate.

Passing Data to a tableViewController depending on button that was pressed

i'm a beginner in swift, but I do have a decent background in HTML,CSS, and some Java. I made this account to literally just ask this question, so i'll get straight to the point.
I really enjoy swift, but i've literally spent a f*cking week writing this same code over and over again, reviewing countless tutorials, books, guides, etc. To no avail; and i'm not going to lie, i'm getting reaaaaally tired of seeing 10+ errors stack up in my compiler as I keep trying to figure this out.
I have a ViewController, and I have a TableViewController - the ViewController being my home screen. On said viewController, I have two buttons, one is for BuildingItems, the other is BuildingBlocks. Both of which are segues.
on my TableViewController, I have buildBlocks and buildItems, both of which have their arrays defined.
What i'm trying to do, is depending on the button the user chose, I want that to then be the data that's used to populate my tableview. I can populate the tableview specifically by using the normal cell.textLabel?.text = arrayNameHere[indexPath.row] but obviously that doesn't solve my problem.
I hope that you're able to help me out here. I apologize for the crappy grammar and formatting, i've literally spent 4-5 hours a day this passed week trying to get this to work, and i've ended up just getting angry and deleting my entire project three different times. I'll include the code below. Again, excuse the formatting. I dont know why I can't seem to figure out swift, I keep trying every variation of delegate to try and pass something over that will let it work, but nothing is working.
ViewController:
#IBAction func BuildItems (_sender: Any) {
}
#IBAction func BuildBlocks (_sender: Any) {
}
In TableViewController:
let buildItems = ["these are all my", "arrays for this"]
let buildBlocks = ["same thing here","just saving time"]
class TableViewController: UITableViewController {
var myIndex = 0
override func tableView(_ tableView: UITableView, numberOfRowsInSection: Int) -> Int {
return buildItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell" for: indexPath)
cell.textLabel?.text = buildItems[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex = indexPath.row
}
}
You only need 1 segue: from the ViewController to the TableViewController.
In your ViewController make a variable for which button is pressed:
var category = ""
Change the value when a button is pressed, then segue:
#IBAction func BuildItems (_sender: Any) {
category = "BuildItems"
self.performSegue(withIdentifier: "segue", sender: self)
}
#IBAction func BuildBlocks (_sender: Any) {
category = "BuildBlocks"
self.performSegue(withIdentifier: "segue", sender: self)
}
Then in prepare:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue" {
let vc = segue.destination as! TableViewController
// Assuming you created a variable called sentCategory in the TableViewController
vc.sentCategory = category
}
}
In your TableViewController create an empty array:
let data = [String]()
In viewDidLoad:
if sentCategory == "BuildItems" {
data = buildItems
} else {
data = buildBlocks
}
Populate your TableView with the data variable.
A couple comments:
Keep everything in your class scope, unless you have a good reason not to.
You don't need delegates in this scenario. Pass data with a prepare function as shown above.
Create bool var in your tableView controller maybe and helpArray
var dataArray:[String] = []
var isBuildItem:Bool = false
In your first controller implement prepareForSegue method like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? TableViewController {
//set true or false it depends of which button you click
vc.isBuildItem = //true or false
}
}
Then in viewDidLoad in your TableViewController check bool variable
if(isBuildItem:Bool){
dataArray = buildItems
}
else{
dataArray = buildBlocks
}
In all your tableView delegate methods change buildItems array with dataArray
in your ViewController:
import UIKit
var segueName = ""
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
segueName = segue.identifier!
print(segueName)
}
}
TableViewController:
import UIKit
class TableViewController: UITableViewController
{
let animals = ["Cat","Dog","Mouse"]
let colors = ["White","Blue","Red"]
var arrayResult = [""]
override func viewDidLoad()
{
super.viewDidLoad()
if segueName == "seguebuttonA"
{
arrayResult = animals
}
else if segueName == "seguebuttonB"
{
arrayResult = colors
}
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int
{
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
// #warning Incomplete implementation, return the number of rows
return arrayResult.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellTest", for: indexPath) as! TableViewCell
// Configure the cell...
cell.titleLabel.text = arrayResult[indexPath.row]
return cell
}
}
Add segue name:
The complete example is in the link below, I hope I have helped, if you have any questions let me know.
https://github.com/cwilliams26/viewSegueTabelview/tree/master

fatal error: unexpectedly found nil while unwrapping an Optional value: Swift, Core Data

I am getting error on the line:
let indexPath = self.menuTable.indexPathForSelectedRow()!.
Seems that I am not getting a value from indexPathForSelectedRow. I am parsing from a CSV file into Core Data. Not sure if it matters. I am new to coding, so not sure if I am missing something obvious.
import UIKit
import CoreData
class MenuTableViewController: UITableViewController {
#IBOutlet var menuTable: UITableView!
private var menuItems:[MenuItem] = []
var fetchResultController:NSFetchedResultsController!
override func viewDidLoad() {
super.viewDidLoad()
// Load menu items from database
if let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext {
let fetchRequest = NSFetchRequest(entityName: "MenuItem")
var e: NSError?
menuItems = managedObjectContext.executeFetchRequest(fetchRequest, error: &e) as! [MenuItem]
if e != nil {
println("Failed to retrieve record: \(e!.localizedDescription)")
}
}
// Make the cell self size
self.tableView.estimatedRowHeight = 66.0
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.layoutIfNeeded()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return menuItems.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = menuTable.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MenuTableViewCell
// Configure the cell...
cell.nameLabel.text = menuItems[indexPath.row].name
cell.detailLabel.text = menuItems[indexPath.row].detail
// cell.priceLabel.text = "$\(menuItems[indexPath.row].price as! Double)"
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
self.performSegueWithIdentifier("showFront", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "showFront")
{
var upcoming: CardFrontViewController = segue.destinationViewController as! CardFrontViewController
let indexPath = self.menuTable.indexPathForSelectedRow()!
let titleString = menuItems[indexPath.row].name
upcoming.titleStringViaSegue = titleString
self.menuTable.deselectRowAtIndexPath(indexPath, animated: true)
}
}
}
Since you have an implementation of tableView:didSelectRowAtIndexPath: and the cell is connected to the segue in the storyboard, the segue is happening twice. The second time the segue is performed there would be no selection because you deselect it during the first segue. You can fix this issue by deleting your implementation of tableView:didSelectRowAtIndexPath: or by creating the segue in the storyboard with the view controller itself as the source instead of the cell and leaving your manual invocation of the segue.
I don't know if this is the problem but why are u using self as sender if u need the indexPath?
Try:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
self.performSegueWithIdentifier("showFront", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "showFront")
{
var upcoming: CardFrontViewController = segue.destinationViewController as! CardFrontViewController
let titleString = menuItems[indexPath.row].name
upcoming.titleStringViaSegue = titleString
self.menuTable.deselectRowAtIndexPath(indexPath, animated: true)
}
}
I see you are using a UITableViewController. In a UITableViewController a UITableView is automatically created for you with the needed outlets. You can access it in code via self.tableView. My guess is that you do not connected the IBOutlet for your UITableView called menuTable. So the optional which is nil while unwrapping is not the indexPath but the UITableView.
Fix:
Delete your IBOutlet and everywhere you use the menuTable variable and use self.tableView instead.

Swift UITableView get Selected table item

I intent to connect two UITableView in one ViewController with two different classes with different data and then get the item selected but i can't make it work
AppController.swift
class AppController: UIViewController {
#IBOutlet weak var projects_TableView: UITableView!
#IBOutlet weak var hours_TableView: UITableView!
var projects_DataSource: TableViewProjects?
var hours_DataSource: TableViewHours?
override func viewDidLoad() {
super.viewDidLoad()
projects_DataSource = TableViewProjects()
hours_DataSource = TableViewHours()
projects_TableView.dataSource = projects_DataSource
hours_TableView.dataSource = hours_DataSource
println("Table Sources Success")
}
func TableProjectsSelected() {
println("Selected Project Table")
self.performSegueWithIdentifier("projectTable", sender: AnyObject?())
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "projectTable" {
//do stuff
println("projectTable ID")
}
}
and TableViewProjects.swift
var items: [String] = ["Project 1", "Project 2", "Project 3"]
override init() {
super.init()
}
//TABLE FUNCTIONS
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:UITableViewCell=UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "projectcell")
cell.textLabel!.text = items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("Select From Class")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appController: AppController = storyboard.instantiateViewControllerWithIdentifier("SelectProjectAndHour") as! AppController
appController.TableProjectsSelected()
}
I can't get a response from item selected from TableViewProjects.swift
Maybe you should add these codes below:
projects_TableView.delegate = projects_DataSource
hours_TableView.delegate = hours_DataSource

How to use navigationController to show up a WebView when I choose one cell from the tableView?

When we choose one cell from the tableView, we use the didSelectRowAtIndexPath method to implement the specific operation.
Then how to jump to another view like webView via the navigationController?
I want to use the prepareForSegue to handle this issue like below, and I just know how to pass data from one viewController to another viewController.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!){
var channelC:ChannelController=segue.destinationViewController as ChannelController
channelC.delegate = self
//将channelData传递给ChannelController中去。
channelC.channelData=self.channelData
}
I don't know how to code in the didSelectRowAtIndexPath method when I want to show up another view like WebView?
I just use the storyboard to handle viewController switch thing.
Thanks
Here I create a simple example for you :
import UIKit
class ViewController: UIViewController,UITableViewDataSource, UITableViewDelegate {
var arr: [String] = ["google", "yahoo", "Swift"]
var index : Int = Int()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return arr.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
var identifier : NSString = "Cell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifier) as? UITableViewCell
if !(cell != nil) {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: identifier)
}
cell?.textLabel.text = self.arr[indexPath.row]
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
//store your clicked row into index
index = indexPath.row
// get to the next screen
self.performSegueWithIdentifier("goNext", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "goNext") {
var webViewController = segue.destinationViewController as googleViewController
//switch case for row which you have clicked
switch index{
case 0:
webViewController.url = "https://www.google.co.in/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8"
case 1:
webViewController.url = "https://in.yahoo.com/"
case 2:
webViewController.url = "https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_456"
default:
println("nothing")
}
}
}
}
here is code for your googleViewController.swift
#IBOutlet weak var webView: UIWebView!
var url : String = String()
override func viewDidLoad() {
super.viewDidLoad()
let requestURL = NSURL(string:url)
let request = NSURLRequest(URL: requestURL!)
webView.loadRequest(request)
}
May be this will help you.