table view cells displaying twice and no subtitle is displayed - swift

I'm trying to make a reminders app but when I add a category, the name of the category is displayed in the cell but the description is not and the cell is displayed twice
//this is in the view controller that has the table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allCategories[section].activities.count
}
//sets the title of the cell to the name of the activity and the subtitle to the description of the activity
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "theCell", for: indexPath)
cell.textLabel?.text = allCategories[indexPath.section].activities[indexPath.row].name
cell.detailTextLabel?.text = allCategories[indexPath.section].activities[indexPath.row].description
return cell
}
//this is in the view controller that creates an activity which populates activities
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "createSegue" {
if activityName.text?.isEmpty == false {
if let activityReminder = activityName.text {
allCategories[addButton.tag].activities.append(Activity(name: activityReminder, description: activityDesc.text!, date: datePicker.date))
}
}
}
}
#IBAction func createP(_ sender: Any) {
performSegue(withIdentifier: "createSegue", sender: self)
}
//this populates allCategories
#IBAction func createPressed(_ sender: Any) {
guard categoryNameTF.text == "" else {
allCategories.append(Categories(name: categoryNameTF.text!, description: categoryDescription.text!, activities: []))
dismiss(animated: true, completion: nil)
icon = currentIcon
return
}
}

Related

Is there any better way to send information to child view?

I have a view controller which has a child view. I want to convey information from the view to the child view. To do so, I did this :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "visualize", sender: self)
invoiceNumber = indexPath.row
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! PreviewViewController
vc.invoiceNumber = invoiceNumber
}
But the problem is that the value of invoiceNumber is not updated on the first iteration but on the second. I tried to see what the problem was and found that "invoiceNumber = indexPath.row" runs after "vc.invoiceNumber = invoiceNumber". Please help ! Thanks
Here is the code of printing :
import UIKit
class ViewController: UIViewController {
var invoiceNumber: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(invoiceNumber)
}
}
Sorry I have mistaken :
var customerData: [[customerInformation]] = []
var itemsData: [[Item]] = []
var totalData: [TotalInformation] = []
var invoiceNumber = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
let tabBar = tabBarController as! baseTabBarController
customerData = tabBar.customerData
itemsData = tabBar.itemsData
totalData = tabBar.totalData
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return customerData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let invoiceCell = tableView.dequeueReusableCell(withIdentifier: "invoice", for: indexPath) as! invoiceTableViewCell
invoiceCell.textLabel?.text = (customerData[indexPath.row][0]).input
invoiceCell.detailTextLabel?.text = "Invoice n°" + String(indexPath.row)
invoiceCell.totalLabel.text = (totalData[indexPath.row]).total
return invoiceCell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let tabBar = tabBarController as! baseTabBarController
tabBar.customerData.remove(at: indexPath.row)
//itemData.remove(at: indexPath.row)
tabBar.totalData.remove(at: indexPath.row)
customerData = tabBar.customerData
itemsData = tabBar.itemsData
totalData = tabBar.totalData
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
invoiceNumber = indexPath.row
print(invoiceNumber, "yes")
performSegue(withIdentifier: "visualize", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! PreviewViewController
vc.invoiceNumber = invoiceNumber
}
}
It is the "vc.invoiceNumber = invoiceNumber" which comes before the "invoiceNumber = indexPath.row" as I tried to change the value of invoiceNumber a variable in the view and print out the value in the child view and if showed its value first (I tested -1).
I added the Preview view controller which receives the invoiceNumber :
import UIKit
import WebKit
class PreviewViewController: UIViewController {
#IBOutlet var webPreview: UIWebView!
var invoiceComposer: InvoiceComposer!
var HTMLContent: String!
var invoiceNumber: Int = -1
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(invoiceNumber)
createInvoiceAsHTML()
}
func createInvoiceAsHTML() {
invoiceComposer = InvoiceComposer()
if let tabBar = tabBarController as? baseTabBarController {
if let invoiceHTML = invoiceComposer.renderInvoice(invoiceNumber: String(invoiceNumber), invoiceDate: "", recipientInfo: tabBar.customerData[invoiceNumber][0].input, items: tabBar.itemsData[invoiceNumber], totalAmount: tabBar.totalData[invoiceNumber].total) {
webPreview.loadHTMLString(invoiceHTML, baseURL: NSURL(string: invoiceComposer.pathToInvoiceHTMLTemplate!)! as URL)
HTMLContent = invoiceHTML
}
}
else {
print("tabBarController is not of type baseTabBarController or either nil ")
}
}
}
Change the order of execution, like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
invoiceNumber = indexPath.row
performSegue(withIdentifier: "visualize", sender: self)
}
I should have connected the segue from the view controller to the child view and not from the cell to the child view.

Use segue for item selected in table view

I've looked at other possible solutions here on StackOverflow but don't quite think they apply to my issue or simple I don't understand is the greater possibility. Here is what I am trying to do. I have a tableview with sections and items in each section. I want to segue to a different view controller based on the selection made by the user in the tableview. I have set up a sample with the following code with segues "report1Segue" and "report2Segue" as an example.
class ViewController: UIViewController {
#IBOutlet weak var reportTableView: UITableView!
let reportGroup = ["Dietary", "Weight", "Sleep", "Meditation", "Fitness"]
let report = [["Calorie Breakdown", "Net Calories"], ["Last 3 Months"], ["Last 3 Months"],["Last 3 Months"],["Exercises by Type"]]
func numberOfSections(in tableView: UITableView) -> Int {
return report.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return reportGroup[section]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "reportSegue" {
if let reportVC = segue.destination as? Report1ViewController, let indexPath = reportTableView.indexPathForSelectedRow {
reportVC.dataToDisplay = report[indexPath.section][indexPath.row]
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return report[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = report[indexPath.section][indexPath.row]
return cell
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "report1Segue", sender: nil)
}
}
Any guidance is always greatly appreciated.
Swift 5.2
For fixed data in a UITableView you can do it like this:
// Hard code your IndexPath references
// This will make the code easier to read in the delegate methods
enum TableIndex {
static let calorieBreakdown = IndexPath(row: 0, section: 0)
static let netCalories = IndexPath(row: 1, section: 0)
static let weightLastThreeMonths = IndexPath(row: 0, section: 1)
static let sleepLastThreeMonths = IndexPath(row: 0, section: 2)
static let meditationLastThreeMonths = IndexPath(row: 0, section: 3)
static let fitnessExercises = IndexPath(row: 0, section: 4)
}
// Example - perform whatever Segues you actually need
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath {
case TableIndex.calorieBreakdown:
performSegue(withIdentifier: "report1Segue", sender: self)
case TableIndex.weightLastThreeMonths, TableIndex.sleepLastThreeMonths:
performSegue(withIdentifier: "report2Segue", sender: self)
default:
break
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let indexPath = tableView.indexPathForSelectedRow else { return }
let data = report[indexPath.section][indexPath.row]
switch segue.identifier {
case "report1Segue":
if let dest = segue.destination as? Report1ViewControler {
dest.dataToDisplay = data
}
case "report2Segue":
if let dest = segue.destination as? Report2ViewControler {
dest.dataToDisplay = data
}
default:
break
}
}
It seems like what needs to happen here is 1, in your didSelectRowAt, you need to figure out what is tapped, So based on how you are displaying the cells, you need to do the same here to find out what cell is tapped. Then based on that selection you will either have a switch case or if else statements that will each call its unique performSegue so that based on a cell selection it will go to a different selection.
For your example, you 'selection' is report[indexPath.section][indexPath.row]
then if switch or if else
if will be like
if selection == "Last 3 Months" || selection == "Net Calories" {
performSegue(withIdentifier: "report1Segue", sender: nil)
} else if selection == "Calorie Breakdown" {
performSegue(withIdentifier: "report2Segue", sender: nil)
}
Hope this helps you out.

If cart button' is clicked from same item then no new row will be added to table view else button is clicked from different items add new row

I want to display table view cellForRow at index path function with the condition
if Add to Button is clicked from same item from second view controller then row in the third controller(Cart View Controller) will be same
else if Add to Button is clicked for different item then new row will be added in table view
Detail View Controller Code:
#IBAction func addToCartTapped(_ sender: UIButton) {
performSegue(withIdentifier: "goToCartView", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToCartView" {
let desVC = segue.destination as! CartViewController
desVC.cartImage = imgImage.image!
desVC.cartImageTitle = lblLabel.text!
}
Cart View Controller Code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemsCell", for: <#T##IndexPath#>) as! CartTableViewCell
cell.imageCell.image = cartImage
cell.titleCell.text = cartImageTitle
cell.priceCell.text = cartPrice
return (cell)
}
Screenshot

Calling specific table section and row for alternate segue? (programmatically)

I am having an issue with calling a specific table section and row using dynamic cells. The table has a navigation controller embedded within it and existing cells use a segue titled 'detailSegue' to 'LibraryViewController'. I would like for a specific cell section and row to redirect to an alternate view controller with a segue titled 'webSegue' to 'WebViewController'.
This is my current code:
class LibraryTableViewController: UITableViewController {
var selectedFileName = ""
struct Objects {
var sectionName : String
var sectionObjects : [String]
var sectionImages : [String]
}
var objectArray = [Objects]()
override func viewDidLoad() {
super.viewDidLoad()
objectArray = [Objects(sectionName: "Led Zeppelin", sectionObjects: ["Houses Of The Holy", "Physical Graffiti", "Presence"], sectionImages: ["Houses Of The Holy", "Physical Graffiti", "Presence"]),
Objects(sectionName: "Dave Matthews Band", sectionObjects: ["Crash", "Before These Crowded Streets", "Everyday"], sectionImages: ["Crash", "Before These Crowded Streets", "Everyday"]),
Objects(sectionName: "Drake", sectionObjects: ["Scorpion", "Views", "So Far Gone"], sectionImages: ["Scorpion", "Views", "So Far Gone"]),
Objects(sectionName: "Jay-Z", sectionObjects: ["The Black Album", "American Gangster", "The Blueprint"], sectionImages: ["The Black Album", "American Gangster", "The Blueprint"]),
Objects(sectionName: "Others", sectionObjects: ["Other Albums", "User Guide"], sectionImages: ["Other Albums", "User Guide"])
]
}
override func numberOfSections(in tableView: UITableView) -> Int {
return objectArray.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectArray[section].sectionObjects.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "libraryCell", for: indexPath)
cell.textLabel?.text = objectArray[indexPath.section].sectionObjects[indexPath.row]
cell.imageView?.image = UIImage(named: objectArray[indexPath.section].sectionImages[indexPath.row])
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return objectArray[section].sectionName
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedFileName = objectArray[indexPath.section].sectionObjects[indexPath.row]
if indexPath.section == 4 && indexPath.row == 1
{
performSegue(withIdentifier: "webSegue", sender: self)
}
else
{
performSegue(withIdentifier: "detailSegue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
let objLibraryViewController = segue.destination as! LibraryViewController
objLibraryViewController.fileName = selectedFileName
}
}
I have instituted an 'if' statement in 'didSelectRow';
if indexPath.section == 4 && indexPath.row == 1
{
performSegue(withIdentifier: "webSegue", sender: self)
}
else
{
performSegue(withIdentifier: "detailSegue", sender: self)
}
yet the particular cell I am calling still uses the 'detailSegue' that brings me to the LibraryViewController.
The only cell that I need redirected is the "Other Albums" cell which happens to be the 4th section and 1st row.
What am i missing that causes the segue to my WebViewController to disregarded?
You have to distinguish the segues also in prepare(for, the web segue is performed without changes.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSegue" {
let objLibraryViewController = segue.destination as! LibraryViewController
objLibraryViewController.fileName = selectedFileName
}
}

Get cell index from tableviewcell

Hey I have this tableview controller that list restaurants from my database. what I want to do is if the cell/row is selected it opens a more detail page about the restaurant. for some reason I can't retrieve the index or its not going to the func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) to give me index here is what I have:
class HomeTableViewController: UITableViewController{
var restaurantArray = [Restaurant]()
var resTransfer: Restaurant?
var resName: String?
var dataBaseRef: FIRDatabaseReference! {
return FIRDatabase.database().reference()
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Home"
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Main Menu", style: .plain, target: self, action: #selector(SSASideMenu.presentLeftMenuViewController))
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
fetchRestaurants()
}
func fetchRestaurants(){
FIRDatabase.database().reference().child("AthensRestaurants/Restaurants").observe(.value, with: { (snapshot) in
var results = [Restaurant]()
for res in snapshot.children{
let res = Restaurant(snapshot: res as! FIRDataSnapshot)
results.append(res)
}
self.restaurantArray = results.sorted(by: { (u1, u2) -> Bool in
u1.name < u2.name
})
self.tableView.reloadData()
}) { (error) in
print(error.localizedDescription)
}
}
// MARK: - Table view data source
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return restaurantArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "restaurantsCell", for: indexPath) as! RestaurantsTableViewCell
// Configure the cell...
cell.configureCell(res: restaurantArray[indexPath.row])
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Row \(indexPath.row)selected")
resTransfer = restaurantArray[indexPath.row]
resName = restaurantArray[indexPath.row].name
print(resName as Any)
performSegue(withIdentifier: "RestaurantDetailView", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "RestaurantDetailView") {
let vc = segue.destination as! RestaurantDetailViewController
vc.resNam = restaurantArray.first?.name //instead of first should be index of cell!
print(vc.resNam! as String)
}
}
}
I am doing it like this, without segue:
func showRestaurantViewControllerWith(_ id: String) {
let storyBoard = UIStoryboard(name: "RestaurantCard", bundle: nil)
let destinationVC = storyBoard.instantiateViewController(withIdentifier: "RestaurantCard") as! RestaurantCardViewController
destinationVC.restaurant.id = id
self.navigationController?.pushViewController(destinationVC, animated: true)
}
and
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.showRestaurantViewControllerWith(self.allRestaurants[indexPath.row].id)
}
You have to use this function to fetch the index
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
print(indexPath.row)
}
It will give you the index.
First of all your code is obviously Swift 3 so the signature of didSelectRowAt is wrong.
Another solution is to pass the indexpath as sender
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row \(indexPath.row)selected")
performSegue(withIdentifier: "RestaurantDetailView", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "RestaurantDetailView" {
let indexPath = sender as! IndexPath
let restaurant = restaurantArray[indexPath.row]
let resName = restaurant.name
print(resName!)
let vc = segue.destination as! RestaurantDetailViewController
vc.resNam = resName
}
}
or still easier to connect the segue to the table view cell. Then you can delete didSelectRow... because prepare(for is called directly and the cell is passed as the sender parameter.
PS: Why is the property name optional? In real life have you ever heard about a restaurant without a name?