I am working on one part of my app which will function as a journal for the user, in which they can add an entry, then review or delete them afterwards. I followed a few Swift tutorials for CoreData and I have confirmed that the CoreData setup is working. However, I am having trouble getting values to pass to the UIViewController that will display the journal after already being saved. In one configuration I had tried it displayed no data, in another the data would stay the same no matter what entry selected, and in the setup below, this line journalEntryViewer.journalViewerTextView.text = journalTextToPass returns "found nil when unwrapping optional value."
Here is the code for the UITableViewController that acts as the list:
import UIKit
import CoreData
class JournalCollectionViewer: UITableViewController {
var JournalEntry = [NSManagedObject]()
var journalTextToPass:String!
var journalTitleToPass:String!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
self.tableView.reloadData()
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "JournalEntry")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
JournalEntry = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
// MARK: - Table view data source
#IBOutlet weak internal var journalCollectionTable: UITableView!
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return JournalEntry.count
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell =
tableView.dequeueReusableCellWithIdentifier("JournalCell")
let entry = JournalEntry[indexPath.row]
cell!.textLabel!.text =
entry.valueForKey("journalDate") as? String
return cell!
}
func selectedTableCell(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let entry = self.JournalEntry[indexPath.row]
self.performSegueWithIdentifier("ShowEntryViewer", sender: entry)
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowEntryViewer" {
let journalEntryViewer = segue.destinationViewController as! JournalEntryViewer
journalEntryViewer.self.navigationItem.title = journalTitleToPass
journalEntryViewer.journalViewerTextView.text = journalTextToPass
}
}
}
In the Swift file for the viewer (journalEntryViewer), all I have done is declared the class and the UITextView. Needless to say, I am at a loss. Any help appreciated!
When you assign value to the IBOutlet object in the prepareForSegue method that time it is not initialized, so you need to pass the string object and assign that string object to the journalViewerTextView in the viewDidLoadof JournalEntryViewer.
First declare two instance var in the JournalEntryViewer like this
var journalTitle: String?
var journalText: String?
Now use this var in viewDidload
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = journalTitle
self.journalViewerTextView.text = journalTitle
}
Now pass this journalTitle and journalText in the prepareForSegue method of JournalCollectionViewer
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowEntryViewer" {
let journalEntryViewer = segue.destinationViewController as! JournalEntryViewer
journalEntryViewer.journalTitle = journalTitleToPass
journalEntryViewer.journalText = journalTextToPass
}
}
New Edit:
Problem with your code is you have not used delegate method didSelectRowAtIndexPath instead of you are using something else selectedTableCell that is wrong try to implement delegate method of UITableView
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let entry = self.JournalEntry[indexPath.row]
self.performSegueWithIdentifier("ShowEntryViewer", sender: entry)
}
Now you are passing entry object so change your prepareForSegue like this
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowEntryViewer" {
let journalEntryViewer = segue.destinationViewController as! JournalEntryViewer
let entry = sender as! NSManagedObject
journalEntryViewer.journalTitle = entry.valueForKey("journalDate") as? String
journalEntryViewer.journalText = entry.valueForKey("journalEntryText") as? String
}
}
Related
I'm performing a segue from one table view to another.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
NSLog("You selected cell number: \(indexPath.row)!")
self.performSegue(withIdentifier: "types", sender: productList[indexPath.row])
}
It should run the viewDidLoad() of the new TableView described by the custom class of the ViewController (Which I've declared in Storyboard)
func viewDidLoad(parent: String) {
print("This should print")
super.viewDidLoad()
//self.typeTableView.delegate = self
//self.typeTableView.dataSource = self
//Set reference
ref = Database.database().reference()
//Retrieve posts
handle = ref?.child(parent).observe(.childAdded, with: { (snapshot) in
let product = snapshot.key as? String
if let actualProduct = product
{
self.productList.append(actualProduct)
self.typeTableView.reloadData()
}
})
}
Any Idea why this might be happening?
Embed navigation controller to your destination controller
and make a segue from current table views cell to it with identifier types.
Then add below method after your
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "types" {
if let indexPath = tableView.indexPathForSelectedRow {
let object = productList[indexPath.row] as! yourProductType
let controller = (segue.destination as! UINavigationController).topViewController as! YourDestinationViewController
controller.yourProductProperty = object
}
}
}
Make sure to declare yourProductProperty in your Destination controller so that you can access current product object in it.
viewDidLoad has no parameters and needs an override clause:
override func viewDidLoad() {
...
}
Your method signature is
func viewDidLoad(parent: String) {
but it should be
override func viewDidLoad() {
super.viewDidLoad()
// Your code
}
are you using same class to tableviewcell ? if yes then keep different identifier [RESUE IDENTIFIER] for both tableview.
I'm trying to pass the event Object into DetailViewController, but it crashes and says: "fatal error: unexpectedly found nil while unwrapping an Optional value". I'm positive the error is in my prepare(for segue:) function, but I've been at this for hours and I can't find out how to fix it. Any help would be appreciated. Thanks!
Here is my code:
class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var tableView: UITableView!
var events = [Event]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
parseCSV()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func logOutAction(sender: AnyObject) {
if FIRAuth.auth()?.currentUser != nil {
do {
try FIRAuth.auth()?.signOut()
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SignUp")
present(vc, animated: true, completion: nil)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
func parseCSV() {
let path = Bundle.main.path(forResource: "eventData", ofType: "csv")
do {
let csv = try CSV(contentsOfURL: path!)
let rows = csv.rows
for row in rows {
let eventTitle = row["Title "]!
let eventLoc = row[" Location "]!
let eventStart = row[" Start_Time "]!
let eventEnd = row [" End_Time"]!
let event = Event(title: eventTitle, loc: eventLoc, start: eventStart, end: eventEnd)
events.append(event)
}
} catch let err as NSError {
print(err.debugDescription)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as? EventCell{
let event = events[indexPath.row]
cell.updateUI(event: event)
return cell
} else {
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let event = events[indexPath.row]
performSegue(withIdentifier: "detailSegue", sender: event)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSeque" {
if let detailVC = segue.destination as? DetailViewController {
if let event = sender as? Event {
detailVC.event = event
}
}
}
}
}
My DetailViewController class:
class DetailViewController: UIViewController {
var event: Event!
#IBOutlet weak var eventTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
eventTitle.text = event.title // error here. says event is nil.
}
}
Since the segue is connected from your cell, it already performed automatically when you tap the cell. Performing it programmatically is resulting in your segue being performed twice: once with the value passed correctly and once without.
You can either change your segue to be connected from the view controller instead of the cell in which case your current code would work, or update your code to do all of the logic in prepareForSegue and remove your implementation of didSelectRowAt:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSeque" {
if let detailVC = segue.destination as? DetailViewController {
let row = tableView.indexPathForSelectedRow!.row
detailVC.event = events[row]
}
}
}
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 6 years ago.
I have a UITableViewController showing all my users from Firebase in a list. If you tap on one user you see another UITableViewController with a static TableView Layout prepared in the Interface Builder to edit the user properties. I want to pass the UID of the selected user to the DetailTableViewController to load all of the current user data there.
EDIT: This question is not an exact duplicate. I want to pass data from one UITableViewController to another UITableViewController not a normal Detail UIViewController!
This is my current code of the first TableViewController.
Can somebody help me? I don't get it.
UserListTableViewController.swift:
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class UserListTableViewController: UITableViewController {
var dbRef:FIRDatabaseReference!
var user = [User]()
var writeSelectedUID:String!
var selectedUID: String = "Mister X"
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.startObservingDB()
}
func startObservingDB () {
dbRef.observe(.value, with: { (snapshot:FIRDataSnapshot) in
var newUser = [User]()
for user in snapshot.children {
let userObject = User(snapshot: user as! FIRDataSnapshot)
newUser.append(userObject)
}
self.user = newUser
self.tableView.reloadData()
}) { (error:Error) in
print(error.localizedDescription)
}
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return user.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "User Cell", for: indexPath) as! CustomUserListTableViewCell
// Configure the cell
let userRow = user[indexPath.row]
cell.userFirstLastNameLabel?.text = "\(userRow.firstName!) \(userRow.lastName!)"
cell.userUsernameLabel?.text = "#\(userRow.username!)"
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//let selectedUID = user[indexPath.row]
let selectedUserRow = user[indexPath.row]
self.writeSelectedUID = "\(selectedUserRow)"
performSegue(withIdentifier: "editUser", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
let viewcontroller = segue.destination as! ManageUserSettingsTableViewController
// Pass the selected object to the new view controller.
if(segue.identifier == "editUser") {
viewcontroller.usernameTextField.text! = "\(self.writeSelectedUID)"
print("Var: \(self.writeSelectedUID)")
}
}
}
In UserListTableViewController.swift :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
let viewcontroller = segue.destination as! ManageUserSettingsTableViewController
// Pass the selected object to the new view controller.
//let object = self.writeSelectedUID as? String
// let object = self.writeSelectedUID as! String!
if(segue.identifier == "editUser") {
if let object = self.writeSelectedUID {
viewcontroller.detailItem = object as AnyObject?
}
}
}
In your AnotherViewController :
var detailItem: AnyObject?
usernameTextField.text = detailItem?.description
I'm learning swift, and i'm having problems trying to fire a segue when a TableViewCell is touched, which is supposed to pass an url to a second view that for the moment just a displays it in a label.
i Dynamically create ( i've seen people use Programmatically, which is probably the right word) every single Cell, so, in the storyboard i don't have any object to link to another view except the view itself... and that's what i did.
So i connected the first view controller to the second, and added the code to perform the segue.
i'm not sure if it's right, my knowledge comes from tutorials that didn't exactly explain what i wanted to do.
down there there's the code of the two views.
first view
import UIKit
protocol sendInfoDelegate{
func userDidEnterInfo( WhichInfo info : String)
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableData = []
#IBOutlet weak var redditListTableView: UITableView!
var selectedCellURL : String?
var delegate : sendInfoDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// change the link to change the json source
getRedditJSON("http://www.reddit.com/.json")
}
//
//Creates a connection via a task (networktask) then parses the json
//
func getRedditJSON(whichReddit : String){
let mySession = NSURLSession.sharedSession()
let url: NSURL = NSURL(string: whichReddit)
let networkTask = mySession.dataTaskWithURL(url, completionHandler : {data, response, error -> Void in
var err: NSError?
var theJSON = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSMutableDictionary
let results : NSArray = theJSON["data"]!["children"] as NSArray
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.redditListTableView.reloadData()
})
})
networkTask.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//needs to be implemented
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
//creates the whole table
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestCell")
let redditEntry : NSMutableDictionary = self.tableData[indexPath.row] as NSMutableDictionary
cell.textLabel?.text = redditEntry["data"]!["title"] as? String
cell.detailTextLabel?.text = redditEntry["data"]!["author"] as? String
return cell
}
// action to be taken when a cell is selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let redditEntry : NSMutableDictionary = self.tableData[indexPath.row] as NSMutableDictionary
self.selectedCellURL = redditEntry["data"]!["url"] as? String
self.performSegueWithIdentifier("passInfo" , sender: indexPath)
println(self.selectedCellURL!)
if delegate != nil {
let information:String = self.selectedCellURL!
println("ciao")
delegate?.userDidEnterInfo(WhichInfo: information)
self.navigationController?.popViewControllerAnimated(true)
}
second view
import UIKit
class WebPageController : UIViewController, sendInfoDelegate {
var infoFromSVC: String?
#IBOutlet weak var labelVC: UILabel!
func userDidEnterInfo(WhichInfo info: String) {
self.infoFromSVC = info
labelVC.text = infoFromSVC
}
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 prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "passInfo"{
let firstVController : ViewController = segue.destinationViewController as ViewController
firstVController.delegate = self
}
}
}
Thanks.
To pass any data to second view controller you need to implement prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) method in your first view controller and here pass any data to your second view controller through segue.destinationViewController object.
for example
// this method must be in first view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "passInfo" {
var secondViewController : SecondViewController = segue.destinationViewController as SecondViewController
var indexPath = self.tableview.indexPathForSelectedRow() //get index of data for selected row
secondViewController.data = self.dataArray.objectAtIndex(indexPath.row) // get data by index and pass it to second view controller
}
}
The code for getting data in second view controller
override func viewDidLoad() {
super.viewDidLoad()
self.label.text = self.data
}
The data variable must be defined as a property of your second view controller.
So I am trying to create a app that is more or less a reference with static data and using a plist to store the data
I have two TableViewControllers setup on the storyboard and I wanna pass the selected cells rownumber or its data to the next view?
My initial setup:
import UIKit
var categoryNames:NSArray = []
var categorySelected:String = ""
class ListForbTVC: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("listor", ofType: "plist")
let dict = NSDictionary(contentsOfFile: path!)
categoryNames = dict.objectForKey("Category") as NSArray
println(categoryNames.count)
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
For some reason I cannot figure out how to count the number of rows in the array?
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 13
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = categoryNames[indexPath.row] as? String
return cell
}
What should I add here to actually pass the value through the segue? Would be neat to know how to both pass the index of the selected row and how to pass the actual value of it.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "listData" {
}
How do I recieve the information in the next view?
I have done this in obj-c and X-Code 5 but I cannot figure out how to do this in Swift.
Thanks in advance
You can simply declare a property in your second view controller - something like this -
class VC2 : UIViewController {
var someString:String?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if (self.someString != nil) {
println("Some string =\(self.someString)")
}
}
}
Then in your prepareForSegue, simply set it
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "listData" {
let destVC=segue.destinationViewController as VC2
destVC.someString="Some value"
}
}
I have updated my answer as per swift.Please check.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueTest") {
var svc = segue!.destinationViewController as secondViewController;
svc.toPass =data}
}
Please check this http://jamesleist.com/ios-swift-passing-data-between-viewcontrollers/
Let me know if faces issue still.