How to design the array structure for Multiple cells in UITableview? - ios

This is my first ViewController ,when I will click on plus(+) button, it will move to the SecondViewController
This is my SecondViewController, after giving the required field when I will click on save button it should add one more row in my tableview respective cell(executive,seniormanagement,staff). But it's not adding the row in respective cells.
This my code what i have tired
FirstViewController
class TableViewHeader: UIViewController ,UITableViewDelegate,UITableViewDataSource,EmployeeProtocol{
#IBOutlet weak var tableviewHeader: UITableView!
var employee = [ EmployeeStatus(status:"Executive",Name:["Smith","Dev","Aryan"], Date:["Nov 14 1889","Dec 01 1980","Sep 18 1997"]),
EmployeeStatus(status:"SeniorManagement",Name:["Sourav","Smarak"], Date:[" April 10 1879"," Dec 11 1990"]),
EmployeeStatus(status:"Staff",Name:["Smith","Dev","Aryan","Suman"], Date:["Feb 14 1234"," Jan 01 1480","Sep 23 1994","July 10 1991"])]
#IBAction func plusButtonTapped(_ sender: UIButton) {
performSegue(withIdentifier: "showSegue", sender:self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSegue" {
let createSegue = segue.destination as! CreateEmployeeViewController
createSegue.myEmpProtocol = self
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return employee.count
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return employee[section].Name.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let Label = UILabel()
Label.text = employee[section].status
Label.backgroundColor = UIColor.gray
return Label
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableviewHeader.dequeueReusableCell(withIdentifier: "TableHeaderCell", for: indexPath) as! TableViewHeadercell
cell.NameLabel.text = employee[indexPath.section].Name[indexPath.row]
cell.DateLabel.text = employee[indexPath.section].Date[indexPath.row]
return cell
}
func addData(status:String, Name:[String], Date:[String]) {
employee.append(EmployeeStatus(status:status, Name:Name, Date: Date))
tableviewHeader.reloadData()
}
}
SeconViewController
protocol EmployeeProtocol {
func addData(status:String, Name:[String], Date:[String])
}
class CreateEmployeeViewController: UIViewController {
var myEmpProtocol:EmployeeProtocol?
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var birthdayTextField: UITextField!
#IBOutlet weak var StatusSegment: UISegmentedControl!
var name = [String]()
var DOB = [String]()
var status:String = ""
#IBAction func saveButtonTapped(_ sender: UIButton) {
self.name = [nameTextField.text!]
self.DOB = [birthdayTextField.text!]
if StatusSegment.selectedSegmentIndex == 0 {
self.status = "Executive"
} else if StatusSegment.selectedSegmentIndex == 1{
self.status = "SeniorManagement"
} else if StatusSegment.selectedSegmentIndex == 2 {
self.status = "Staff"
}
myEmpProtocol?.addData(status:status, Name:name, Date:DOB)
}
#IBAction func CancelButtonTapped(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
MyClass
import Foundation
struct EmployeeStatus {
var status:String
var Name:[String]
var Date:[String]
init(status:String, Name:[String], Date:[String]){
self.status = status
self.Name = Name
self.Date = Date
}

Your current code is adding the new EmployeeStatus to employee array, not the item's EmployeeStatus.Name array and EmployeeStatus.Date array.
Please try this code.
func addData(status:String, Name:[String], Date:[String]) {
var arr = [EmployeeStatus]()
employee.forEach { ele in
if ele.status == status {
var e = ele
e.Name.append(Name[0])
e.Date.append(Date[0])
arr.append(e)
} else {
arr.append(ele)
}
}
employee = arr
tableviewHeader.reloadData()
}

Your data looks interesting. It might work better with
enum Level: Int {
case executive = 0, seniorManagement, staff
var stringValue: { return (String:description: self).capitalizingFirstLetter }
}
struct Employee {
let name: String
let date: String
}
var employees: [Level: [Employee]] = [
.executive: [Employee(name: "Smith", date: "Nov 14 1889"],
.seniorManagement: [],
.staff: []]
Then you can add employees with:
guard let level = Level(rawValue: status) else {
throw Errors.levelDoesNotExist(status)
}
employees[.level] = newEmployee
Reloading the tableView works as you do already. Just the data is in a format where its weird to add to.
And best dismiss the second viewController once its finished after the addData delegate call so its going back to the first to show the updated data.

You have to set your view controller as a table view delegate and datasource.
Add to the end of the viewDidLoad at TableViewHeader class:
override func viewDidLoad() {
super.viewDidLoad()
tableviewHeader.delegate = self
tableviewHeader.dataSource = self
}

In this way, you can pass value from one view controller to another tab:
First of all open main.storyboard
Select the First view controller and drag and drop UItextField and UIButton on the field.

Related

How to pass data with delegate from footer cell to view controller?

Ive been stuck trying to pass data from the FoodEatenController(FEC) Footer to the TotalCaloriesController. The code that I have now it shows NOTHING in the calorieLbl of the TotalCalorieController(TCC).
The delegate that ive been using to pass the data from the FEC to the TCC does not pass the text/string data that is in the FoodFooter calorieTotallbl to the TEC calorieLbl
the data that populates the cells of the FEC is retrieved from Cloud Firestore and passed in from anotherView Controller (FoodPickerController)
import UIKit
class FoodEatenController: UIViewController{
var selectedFood: FoodList! // allows data to be passed into the VC
// allows data to be sepearted into sections
var foodItems: [FoodItem] = []
var groupedFoodItems: [String: [FoodItem]] = [:]
var dateSectionTitle: [String] = []
#IBOutlet weak var tableView: UITableView!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? TotalCalorieController {
}
}
}
extension FoodEatenController: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return dateSectionTitle.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let date = dateSectionTitle[section]
return groupedFoodItems[date]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let foodCell = tableView.dequeueReusableCell(withIdentifier: "FoodCell") as! FoodCell
let date = dateSectionTitle[indexPath.section]
let foodItemsToDisplay = groupedFoodItems[date]![indexPath.row]
foodCell.configure(withCartItems: fooditemsToDisplay.foodList)
return foodCell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let foodHeader = tableView.dequeueReusableCell(withIdentifier: "FoodHeader") as! FoodHeader
let headerTitle = dateSectionTitle[section]
foodHeader.dateLbl.text = "Date: \(headerTitle)"
return foodHeader
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let foodFooter = tableView.dequeueReusableCell(withIdentifier: "FoodFooter") as! FoodFooter
let date = dateSectionTitle[section]
let arrAllItems = groupedFoodItems[date]!
var total: Float = 0
for item in arrAllItems {
let eaten = item.productList
let selectedMeal = item.foodList.selectedOption
if selectedMeal == 1 {
total = total + (Float(eaten!.calorie))
}
}
foodFooter.calorieTotal.text = String(subtotal!)
foodFooter.delegate = self
return foodFooter
}
}
extension FoodEatenController: EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
let popUp = self.storyboard?.instantiateViewController(withIdentifier: "AdditionalCostsVC") as! TotalCalorieController
popUp.calorieLbl.text = info
}
}
import UIKit
protocol EatenFoodDelegate: class {
func onTouchCaloireInfo(info: String)
}
class FoodFooter: UITableViewCell {
weak var delegate: EatenFoodDelegate? = nil
#IBOutlet weak var calorieTotal: UILabel!
#IBOutlet weak var totalInfoBtn: UIButton!
#IBAction func totalOnClicked(_ sender: AnyObject) {
self.delegate?. onTouchCaloireInfo(info: calorieTotal.text!)
}
}
class TotalCalorieController: UIViewController, EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
calorieLbl.text = info
}
#IBOutlet weak var calorieLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func returnButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
print("Close Taxes and Fees")
}
}
Add the following line at the end of the func onTouchCaloireInfo(info:)
self.present(popUp, animated: true, completion: nil)
If you would like to be sure that the function onTouchCaloireInfo(info:) gets called, just add the following line:
debugPrint("onTouchCaloireInfo")
And check, if it prints the given string in the console of the Xcode
extension FoodEatenController: EatenFoodDelegate {
func onTouchCaloireInfo(info: String) {
debugPrint("onTouchCaloireInfo")
let popUp = self.storyboard?.instantiateViewController(withIdentifier: "AdditionalCostsVC") as! TotalCalorieController
self.present(popUp, animated: true) {
popUp.calorieLbl.text = info
}
}
}

How to pass using delegates in a tableview through the footer cell?

What Im trying to do is pass data to another view controller through a button in the CalorieFooter cell using a Delegate to pass data in the cell.
I can't successfully pass data through the infoButton in the CalorieFooter cell to show the fat, carbs, and proteins for the calorie consumption of that day in the CalorieBreakdownController.
I am currently getting "0"'s in all labels of the CalorieBreakdownController (as seen in the far right of the image).
I think the issue might be because my calculationDelegate is done in the CalorieFooter cell. and the way I got the subtotal in the cell was by seperating the cells into sections. im a little confused at how to pass data to CalorieBreakdownController from the the footer, to get the "Subtotal" in the labels.
How would I be able to pass the "subTotal" data the CalorieBreakdownController from the calorieFooter cell (as seen in the image below)
thanks in advance for any help that is provided
import UIKit
class CalorieViewController: UIViewController {
var selectedFood: FoodList!
var additionalCalories: DailyCalories! // delegate code for footer
var calorieItems: [DailyCalories] = []
var groupedCalorieItems: [String: [DailyCalories]] = [:]
var dateSectionTitle: [DailyCalories] = []
#IBOutlet weak var calorieTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
groupedFoodItems = Dictionary(grouping: calorieItems, by: {$0.foodList.day})
dateSectionTitle = groupedCalorieItems.map{$0.key}.sorted()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? CalorieTotalController { // delegate code for footer
vc.additionalCalories = self.additionalCalories
}
}
}
extension CalorieViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return dateSectionTitle.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let date = dateSectionTitle[section]
return groupedCalorieItems[date]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let calorieCell = tableView.dequeueReusableCell(withIdentifier: "CalorieCell") as! CalorieCell
let date = dateSectionTitle[indexPath.section]
let calorieItemsToDisplay = groupedCalorieItems[date]![indexPath.row]
calorieCell.configure(withCartItems: calorieItemsToDisplay.foodList)
return calorieCell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let calorieHeader = tableView.dequeueReusableCell(withIdentifier: "CalorieHeader") as! CalorieHeader
let headerTitle = dateSectionTitle[section]
calorieHeader.dateLbl.text = "Date: \(headerTitle)"
return calorieHeader
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let calorieFooter = tableView.dequeueReusableCell(withIdentifier: "CalorieFooter") as! CalorieFooter
let date = dateSectionTitle[section]
let arrAllItems = groupedCalorieItems[dispensary]!
var subtotal: Float = 0
for item in arrAllItems {
if item.foodList.selectedOption == 1 {
subtotal = subtotal + (Float(item.foodList.calorie1) * Float(item.foodList.count))
} else if item.foodList.selectedOption == 2 {
subtotal = subtotal + (Float(item.foodList.calorie2) * Float(item.foodList.count))
} else if item.foodList.selectedOption == 3 {
subtotal = subtotal + (Float(item.foodList.calorie3) * Float(item.foodList.count))
}
}
calorieFooter.cartTotal.text = "\(subtotal)"
calorieFooter.calculationDelegate = self // delegate code for footer
calorieFooter.additionalCalories! = ??? // can't get the right code set to allow the data to pass
calorieFooter.calculations! = ??? // can't get the right code set to allow the data to pass
return calorieFooter
}
}
extension CalorieViewController: CalculationDelegate { // delegate code for footer
func onTouchInfoButton(from cell: CalorieFooter) {
self.additionalCalories = cell.additionalCalories
self.performSegue(withIdentifier: "CalculateDailyCalorieCell", sender: self)
}
}
import UIKit
protocol CalculationDelegate: class { // delegate code for footer
func onTouchInfoButton(from cell: CalorieFooter)
}
class CalorieFooter: UITableViewCell {
weak var calculationDelegate: CalculationDelegate? // delegate code for footer
var additionalCalories: DailyCalories! // delegate code for footer
var calculations: [DailyCalories] = []
#IBOutlet weak var calorieTotal: UILabel!
#IBOutlet weak var additionalFeesBtn: UIButton!
#IBAction func overallTotalBtn(_ sender: Any) {
self.additionalFeesDelegate?.onTouchInfoButton(from: self) // delegate code for footer
}
}
class CalorieTotalController: UIViewController {
var additionalCalories: DailyCalories! // delegate code for footer
var calculations: [DailyCalories] = [] // delegate code for footer
#IBOutlet weak var calorieSubtotal: UILabel!
#IBOutlet weak var fatTotal: UILabel!
#IBOutlet weak var carbTotal: UILabel!
#IBOutlet weak var proteinTotal: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// calculations done for when data is passed through Delegate
var subtotal: Float = 0
for item in calculations {
if item.foodList.selectedOption == 1 {
subtotal = subtotal + (Float(item.foodList.calorie1) * Float(item.foodList.count))
} else if item.foodList.selectedOption == 2 {
subtotal = subtotal + (Float(item.productList.calorie2) * Float(item.foodList.count))
} else if item.foodList.selectedOption == 3 {
subtotal = subtotal + (Float(item.foodList.calorie3) * Float(item.foodList.count))
}
}
let protein = Float(subtotal * 0.25)
let carbs = Float(subtotal * 0.25)
let fat = Float(subtotal * 0.5)
calorieSubtotal.text = String(subtotal!)
proteinTotal.text = String(protein!)
fatTotal.text = String(fat!)
carbTotal.text = String(carbs!)
}
#IBAction func closeButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
print("Calories BreakDown")
}
}
Using delegates is probably a good idea. I just wrote some code (not tested!) to give you an idea how it could work:
Extend the delegate class:
protocol CalculationDelegate: class {
func onTouchInfoButton(from cell: CalorieFooter)
func getAdditionalCalories() -> Int
func getCalculations() -> Int // or whatever type this object should be
}
Make sure your view controller follows the protocol:
extension CalorieViewController: CalculationDelegate {
func onTouchInfoButton(from cell: CalorieFooter) { ... }
func getAdditionalCalories() -> Int {
return self.calories
}
func getCalculations() -> Int {
return self.calculations
}
}
Add local variables to CalorieViewController that stores the current state (amount of calories/calculations)
class CalorieViewController: UIViewController {
private var calories: Int = 0
private var calculations: Int = 0
... other code that is already in the UIViewController
}
Make sure to initialize these variables somewhere! Something like:
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
... same as before...
self.calories = subtotal // I guess?
self.calculations = calculations // put this somewhere where calculations is initialized
}
Now, the data should be available inCalorieFooter. I added the default values 0 for when the calculationDelegate is nil:
let calories = calculationDelegate?.getAdditionalCalories() ?? 0
let calculations = calculationDelegate?.getCalculations() ?? 0
Good luck!
I guess, In viewForFooterInSection() function you forgot to set any value for calorieFooter.additionalCalories !
calorieFooter.additionalCalories should be of type DailyCalories not array of DailyCalories.
For example you can set -
let date = dateSectionTitle[section]
calorieFooter.additionalCalories = date
Without "!" sign, whenever you are assigning some value to any variable you don't need to use ! in the end.

Update initialized Data in array with variable then pass array to next view controller

I'm having issues moving the data from the selected cells from the (service2viewcontroller) to the (confirmorderviewcontroller). I am trying to move the cell data (cells with a stepper.value above 0(var quantity > 0.0 (in Service2ViewController))), I was told to pass the array to the next view controller, to do so for a stepper value above 0 I would need to also send the indexpath.row for the rows with a quantity variable above 0 correct? I don't know how to do this if anyone can help I would greatly appreciate it. also the label is not updating when I use the stepper it stays at 0, can I place the quantity variable inside of the array? the total price label in the view controller continues to function and the data is sent to the (confirmorderviewcontroller) with no issues.
first TableView (data is input and forwarded)
class Service2ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var service2Total: UILabel!
#IBOutlet weak var service2TableView: UITableView!
// service data
var Wash: [Service2] = []
//stepper and price calculation
var quantity = Double()
var totalPrice : Double = 0.0
var priceList = [Int : Double]()
var totalProductPrice = [Int : Double]()
var label : Int!
override func viewDidLoad() {
super.viewDidLoad()
Wash = Options2()
if Int(quantity) > 0{
service2TableView.reloadData()
}
priceList[0] = 3.51//price list
priceList[1] = 5.51
service2Total.text = "$0.00"
}
// create data array
func Options2() -> [Service2]{
var washOptions: [Service2] = []
let option1 = Service2(titled: "Test", pricing: "$3.51", image: #imageLiteral(resourceName: "Wash&Fold"), description:"Testing the description box", quantity: Int(quantity))
let option2 = Service2(titled: "Test", pricing: "$5.51", image: #imageLiteral(resourceName: "Wash&Fold"), description: "Testing the description box", quantity: Int(quantity))
washOptions.append(option1)
washOptions.append(option2)
return washOptions
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Wash.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let Wash1 = Wash[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Service2Cell", for: indexPath) as! Service2TableViewCell
cell.setService(Wash: Wash1)
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 133
}
#IBAction func stepperAcn(_ sender: UIStepper) {
//change label value with stepper
let cellPosition = sender.convert(CGPoint.zero, to: service2TableView)
let indPath : IndexPath = service2TableView.indexPathForRow(at: cellPosition)!
quantity = sender.value
let getCurrentProductPrice : Double = priceList[indPath.row]! * sender.value
totalPrice = gettingPriceLabel(indPath: indPath, getCurrentProductPrice: getCurrentProductPrice)
if totalPrice == 0{
service2Total.text = ("$0.00")
}
else{
service2Total.text = ("$")+String(totalPrice)
}
print("total price",totalPrice)
print("quantity double",quantity)
service2TableView.reloadData()
}
func gettingPriceLabel(indPath: IndexPath, getCurrentProductPrice : Double) -> Double
{
totalProductPrice[indPath.row] = getCurrentProductPrice
var totalCost : Double = 0.0
let valuesArr = Array(totalProductPrice.values)
for i in 0..<valuesArr.count
{
totalCost = totalCost + valuesArr[i]
}
return totalCost
}
// add function to collect (didSelectRowAt) and send selected data to cart and prepare for segue
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
// change sender to
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let DestViewController: ConfirmorderViewController = segue.destination as! ConfirmorderViewController
if totalPrice > 0.00{
DestViewController.amount = totalPrice
}
}
}
service initializer
class Service2
{
var service2Title: String
var service2Image: UIImage
var Service2Pricing: String
var service2Description: String
var service2Quantity: Int
init(titled: String, pricing: String, image: UIImage, description: String, quantity: Int){
self.service2Title = titled
self.Service2Pricing = pricing
self.service2Image = image
self.service2Description = description
self.service2Quantity = quantity
}
}
Service 2 TableViewCell
class Service2TableViewCell: UITableViewCell {
#IBOutlet weak var service2Title: UILabel!
#IBOutlet weak var service2Stepper: UIStepper!
#IBOutlet weak var service2StepperLbl: UILabel!
#IBOutlet weak var service2Pricing: UILabel!
#IBOutlet weak var service2Image: UIImageView!
#IBOutlet weak var service2Description: UILabel!
func setService(Wash: Service2){
service2Image.image = Wash.service2Image
service2Pricing.text = Wash.Service2Pricing.description
service2Title.text = Wash.service2Title
service2Description.text = Wash.service2Description
service2StepperLbl.text = Wash.service2Quantity.description
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Second TableView (receives data)
class ConfirmorderViewController: UIViewController{
#IBOutlet weak var Total: UILabel!
#IBOutlet weak var confirmOrderTableView: UITableView!
var titled = [String]()
var amount: String = ""
//var quantity = String()
var image1 = [UIImage]()
var Price = [Double]()
override func viewDidLoad() {
super.viewDidLoad()
Total.text = amount
confirmOrderTableView.reloadData()
}
}
extension ConfirmorderViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titled.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ConfirmOrderTableViewCell") as! ConfirmOrderTableViewCell
cell.coTitle?.text = titled[indexPath.row]
cell.coImg?.image = image1[indexPath.row]
//cell.coQuantity.text = quantity
cell.coPrice?.text = Price.description
return cell
}
}
I have tried here. I got list of row numbers having more than 0 order. I have it stored in whichRowToBeAdd. If user decreased to Zero, respective rows will removed from this array.
With the help of Singleton Class, we can store whatever we need to show in NextViewController
var whichRowToBeAdd = [Int]() // GLOBAL
#IBAction func stepperAcn(_ sender: UIStepper) {
//change label value with stepper
let cellPosition = sender.convert(CGPoint.zero, to: service2TableView)
let indPath : IndexPath = service2TableView.indexPathForRow(at: cellPosition)!
if Int(sender.value) == 0
{
if whichRowToBeAdd.contains(indPath.row)
{
whichRowToBeAdd.remove(at: whichRowToBeAdd.index(of: indPath.row)!)
}
else
{
}
}
else
{
if whichRowToBeAdd.contains(indPath.row)
{
}
else
{
whichRowToBeAdd.append(indPath.row)
}
}
//....
//..... Your Code in your answer
}
// After stepper Action, final click of Button, which moves to Next ViewController
#IBAction func goToConfirmOrder(_ sender: UIBarButtonItem) {
print("\n\n Val_ ", whichRowToBeAdd)
singleTon.sharedInstance.orderDict.removeAll()
for i in 0..<whichRowToBeAdd.count
{
let indPath = IndexPath(row: whichRowToBeAdd[i], section: 0)
let newCell = tblVw.cellForRow(at: indPath) as! Service2TableViewCell
print("qweqwe ",newCell.testLbl.text)
let name : String = newCell.service2Title.text!
let image : UIImage = newCell.service2Image.image
let quantity : Int = Int(newCell.service2StepperLbl.text!)!
getOrderOneByOne(productName: name, productImage: image, productQuantity: quantity)
if i == (whichRowToBeAdd.count - 1)
{
self.performSegue(withIdentifier: "confirmOrderVC", sender: nil)
}
}
}
func getOrderOneByOne(productName: String, productImage : UIImage, productQuantity: Int)
{
let createDict = ["productName" : productName, "productImage" : productImage, "productQuantity" : productQuantity] as [String : Any]
singleTon.sharedInstance.orderDict.append(createDict)
}
Singleton Class
class singleTon: NSObject {
static let sharedInstance = singleTon() // Singleton Instance
var orderDict = [[String : Any]]() // Dictionary Declaration
}
Next ViewController
class ConfirmOrderViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("\n\norderDict.coun ", singleTon.sharedInstance.orderDict)
}
}
With this, you can display datas in TableView in this ConfirmOrderViewController.

How to make a data structure for multi-level drill-down TableView?

I would like to ask you guys how to make a data structure for multi-level drill-down TableView in Swift. As of myself, I know how to do it and pass data this way TableView -> TableView -> DetailViewController using NSObject for creating Swift models. But several weeks now I struggle with finding of how to do this way: TableView -> TableView -> DetailViewController -> TableView again.
As the app I am doing now is education related, the first TableView would contain sections of topics and leesons in them, then when the user selects the lesson, the segue performs and shows content as ViewController, then the user press the button and he is redirected to another TableView with tasks related to the lesson in it.
So in all it should be like this: LessonsTableView -> LessonDetailViewController -> TasksTableView -> TaskDetailViewController.
I can say that the main problem is to get the tableView of tasks listed from LessonDetailViewController. As I know how to make 2 levels of data, but can't do 3 or more.
I have searched all the internet, I have found examples in Obj-C, but simply I don't understand that programming language (tried to convert the code in XCode). Could anyone guide me of how to achieve this? Perhaps there's a link with brief tutorial that you guys know.
Update
I've managed to do the first TableView and ViewController, but stuck with passing data from LessonViewController to TasksTableView, getting this Error: Expression type '[Lesson]' is ambiguous without more context.
Perhaps anyone could help me with this, my Model:
import Foundation
struct task {
var name: String
}
struct Lesson {
var name: String
var info: String
var tasks: [task]
}
class LessonList {
var name: String
var lessons = [Lesson]()
init(name: String, lessons: [Lesson]) {
self.name = name
self.lessons = lessons
}
class func lessonsSection() -> [LessonList] {
return [self.intro(), self.can()]
}
private class func intro() -> LessonList {
let tasks: [task] = [task(name: "hello"), task(name: "Hey")]
let tasks1: [task] = [task(name: "is He?"), task(name: "are they?")]
var lessons = [Lesson]()
lessons.append(Lesson(name: "I am", info: "This class is I am", tasks: tasks))
lessons.append(Lesson(name: "He is", info: "This class is He is", tasks: tasks1))
return LessonList(name: "Intro", lessons: lessons)
}
private class func can() -> LessonList{
let tasks: [task] = [task(name: "bye"), task(name: "can have")]
let tasks1: [task] = [task(name: "Can he?"), task(name: "They can't")]
var lessons = [Lesson]()
lessons.append(Lesson(name: "I can", info: "This class is I can", tasks: tasks))
lessons.append(Lesson(name: "He can't", info: "This class is He can't", tasks: tasks1))
return LessonList(name: "Can", lessons: lessons)
}
}
The CourseTableView (first)`import UIKit
class CourseTableVC: UITableViewController {
let lessonLists : [LessonList] = LessonList.lessonsSection()
override func viewDidLoad() {
super.viewDidLoad()
title = "Horizon English"
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return lessonLists.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lessonLists[section].lessons.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "lessonCell", for: indexPath)
let lessonList = lessonLists[indexPath.section]
let lessons = lessonList.lessons
let lesson = lessons[indexPath.row]
cell.textLabel?.text = lesson.name
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let lessonList = lessonLists[section]
return lessonList.name
}
//MARK: - UITableViewdelegate, navigation segue
var selectedLesson: Lesson?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let lessonLine = lessonLists[indexPath.section]
var lesson = lessonLine.lessons[indexPath.row]
selectedLesson = lesson
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "lessonDetailView" {
let lessonDetailVC = segue.destination as! LessonViewController
lessonDetailVC.lesson = selectedLesson
}
}
}`
LessonViewController where I get error (in prepare for segue)`import UIKit
class LessonViewController: UIViewController {
#IBOutlet weak var infoLabel: UILabel!
#IBAction func toTasksButton(_ sender: Any) {
}
var lesson: Lesson?
override func viewDidLoad() {
super.viewDidLoad()
title = lesson?.name
infoLabel.text = lesson?.info
}
// MARK: - navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showTasks" {
var taskTV = segue.destination as! TasksTableVC
taskTV.tasksList = lesson?.tasks as! [Lesson]
// Error: Expression type '[Lesson]' is ambiguous without more context
}
}
}`
and TasksTableView: `import UIKit
class TasksTableVC: UITableViewController {
var tasksList = [Lesson]()
override func viewDidLoad() {
super.viewDidLoad()
title = "Tasks"
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasksList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath)
let taskList = tasksList[indexPath.section]
let tasks = taskList.tasks
let task = tasks[indexPath.row]
cell.textLabel?.text = String(describing: task)
return cell
}
}`
Thanks in advance.
I can say that the main problem is to get the tableView of tasks listed from LessonDetailViewController.
I don't know what steps you already did so I'll say how I'd do it:
1 - Have the structure implemented. That's my suggestion:
struct Task {
var name = ""
}
struct Lesson {
var name = ""
var tasks: [Tasks] // list of tasks for this lesson
}
OR
struct Task {
var name = ""
var lesson: Lesson // the leasson which the task belongs to
}
struct Lesson {
var name = ""
}
2 - I suppose you know how to pass data between view controllers, so when the user select the cell of the lesson he wants, you'd pass the lesson object to the LessonDetailVC and show the data. Something like
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedLesson = lessons[indexPath.row]
// code to pass to the next vc here or in prepareForSegue
}
3 - To create the tableview for the tasks you just have to filter(or query, or fetch, I don't know how you're getting the data) in order to obtaining the tasks. Let's suposed you used the first code from item 1 so you just simply show the lesson.tasks array in your table view. If you chose the second code from item 1 you will have to perform some query with a filter passing the lesson your in, something like query.where("lesson", lesson) and you will have the tasks array.
UPDATE
Since you're new on Swift here's an idea on how to model your view controller:
// Your models
struct Task {
var name = ""
var lesson: Lesson // the leasson which the task belongs to
}
struct Lesson {
var name = ""
}
// This is just a DataManager to help you get your data
class DataManager {
func getLessons() -> [Lesson]? {
return [Lesson(name: "Lesson 1"), Lesson(name: "Lesson 2"), Lesson(name: "Lesson 3")]
}
func getTasks(for lesson: Lesson) -> [Task]? {
guard let lessons = getLessons() else {
return nil
}
let allTasks = [
Task(name: "T1", lesson: lessons[0]),
Task(name: "T2", lesson: lessons[0]),
Task(name: "T3", lesson: lessons[0]),
Task(name: "T1", lesson: lessons[1]),
Task(name: "T2", lesson: lessons[1]),
Task(name: "T1", lesson: lessons[2]),
Task(name: "T2", lesson: lessons[2]),
Task(name: "T3", lesson: lessons[2]),
Task(name: "T4", lesson: lessons[2])
]
return allTasks.filter{ $0.lesson == lesson }
}
}
class LessonsViewController: UIViewController, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
private var lessons: [Lesson]? {
didSet {
tableView.reloadData()
}
}
private var selectedLesson: Lesson?
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
lessons = DataManager().getLessons()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lessons?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "lessonCell", for: indexPath)
let lesson = lessons[indexPath.section]
cell.textLabel?.text = lesson.name
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var lesson = lessons[indexPath.row]
selectedLesson = lesson
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "lessonDetailView" {
let lessonDetailVC = segue.destination as! LessonViewController
if selectedLesson != nil {
lessonDetailVC.lesson = selectedLesson!
}
}
}
}
class TasksViewController: UIViewController, UITableViewDataSource {
var lesson: Lesson!
private var tasks: [Task]? {
didSet {
tableView.reloadData()
}
}
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tasks = DataManager().getTasks(for: lesson)
}
}

Cell is not selectable embed UItableView inside main View?

I have UITableView embed inside a main UIView. My problem is when I click on a cell to select items, it is not responding to the selection or triggering the segue.
Note: In attributes inspecter
is selection supposed to be set to single selection?
Update Note: the prepareForSegue function is not triggering or print "test".
import UIKit
import SwiftValidator
import CoreData
class EditNpFormViewController: UIViewController,UITextFieldDelegate, UIViewControllerTransitioningDelegate{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(EditNpFormViewController.dismissKeybored)))
self.npVisitViewTable.dataSource = self
self.npVisitViewTable.delegate = self
loadValidaValidationSettings()
loadUIObjectsSetting()
populateUIobjects()
}
func loadValidaValidationSettings() {
validator.styleTransformers(success:{ (validationRule) -> Void in
// clear error label
validationRule.errorLabel?.hidden = true
validationRule.errorLabel?.text = ""
if let textField = validationRule.field as? UITextField {
textField.layer.borderColor = UIColor.greenColor().CGColor
textField.layer.borderWidth = 0.5
}
}, error:{ (validationError) -> Void in
print("error")
validationError.errorLabel?.hidden = false
validationError.errorLabel?.text = validationError.errorMessage
if let textField = validationError.field as? UITextField {
textField.layer.borderColor = UIColor.redColor().CGColor
textField.layer.borderWidth = 1.0
}
})
validator.registerField(firstNameTextField, errorLabel:firstNameErrorLabel , rules: [RequiredRule(), AlphaRule()])
validator.registerField(lastNameTextField, errorLabel:lastNameErrorLabel , rules: [RequiredRule(), AlphaRule()])
validator.registerField(brnTextFieled, errorLabel:brnErrorLabel, rules: [RequiredRule(), AlphaNumericRule()])
}
func loadUIObjectsSetting(){
self.firstNameTextField.delegate = self
self.lastNameTextField.delegate = self
self.brnTextFieled.delegate = self
self.pickerLhsc.dataSource = self
self.pickerLhsc.delegate = self
self.pickerLhsc.tag = 0
self.ltchTextFieled.inputView = pickerLhsc
self.ltchTextFieled.delegate = self
self.ltchTextFieled.hidden = true
}
func populateUIobjects(){
self.firstNameTextField.text = selectedNpForm!.patientFirstName
self.lastNameTextField.text = selectedNpForm!.patientLastName
self.brnTextFieled.text = selectedNpForm!.brnNumber
self.ltchTextFieled.text = pickerDataLtch.filter { $0.uniqId == selectedNpForm!.ltch }.first?.hospital ?? ""
self.isPatientLtchResidentSwitch.setOn((selectedNpForm!.isPatientLtchResident == 0 ?false : true), animated: true)
self.ltchTextFieled.hidden = !(isPatientLtchResidentSwitch.on ? true : false)
reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: - Methods
func dismissKeybored(){
firstNameTextField.resignFirstResponder()
lastNameTextField.resignFirstResponder()
brnTextFieled.resignFirstResponder()
ltchTextFieled.resignFirstResponder()
}
func textFieledShouldReturn(){
firstNameTextField.resignFirstResponder()
lastNameTextField.resignFirstResponder()
brnTextFieled.resignFirstResponder()
ltchTextFieled.resignFirstResponder()
}
func hideKeyboard(){
self.view.endEditing(true)
}
func validationSuccessful() {
print("Validation Success!")
}
func validationFailed(errors:[(Validatable, ValidationError)]) {
print("Validation FAILED!")
// turn the fields to red
for (field, error) in errors {
if let field = field as? UITextField {
field.layer.borderColor = UIColor.redColor().CGColor
field.layer.borderWidth = 1.0
}
error.errorLabel?.text = error.errorMessage // works if you added labels
error.errorLabel?.hidden = false
}
}
func reloadData(predicate: NSPredicate? = nil) {
if let selectedNpForm = selectedNpForm {
if let formVisits = selectedNpForm.visits?.allObjects {
npVisits = formVisits as! [NpVisit]
}
} else {
let fetchRequest = NSFetchRequest(entityName: "NpVisit")
fetchRequest.predicate = predicate
do {
if let results = try moc.executeFetchRequest(fetchRequest) as? [NpVisit] {
npVisits = results
}
} catch {
fatalError("There was an error fetching the list of devices!")
}
}
npVisitViewTable.reloadData()
}
//MARK: - #IBAction
#IBAction func EditBrn(sender: AnyObject) {
print("Validating...")
//validator.validate(self)
}
#IBAction func saveNpForm(sender: AnyObject) {
if let selectedNpFormId = moc.objectWithID(selectedNpForm!.objectID) as? NpForm{
selectedNpFormId.brnNumber = brnTextFieled.text
selectedNpFormId.patientFirstName = firstNameTextField.text
selectedNpFormId.patientLastName = lastNameTextField.text
selectedNpFormId.isPatientLtchResident = isPatientLtchResidentSwitch.on ? true : false
selectedNpFormId.ltch = hospitalUniqId
}
do {
try moc.save()
} catch let error as NSError {
print("Could not save \(error), \(error)")
}
}
#IBAction func dosePatientResideLtch(sender: AnyObject) {
self.ltchTextFieled.hidden = !(isPatientLtchResidentSwitch.on ? true : false)
}
// MARK: Validate single field
// Don't forget to use UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
validator.validateField(textField){ error in
if error == nil {
// Field validation was successful
} else {
// Validation error occurred
}
}
return true
}
//MARK: - Segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "addNewNoVisit"){
if let distination = segue.destinationViewController as? MapViewController{
distination.clientNpForm._patientFirstName = firstNameTextField.text!
distination.clientNpForm._patientLastName = lastNameTextField.text!
distination.clientNpForm._brnNumber = brnTextFieled.text!
distination.clientNpForm._isPatientLtchResident = isPatientLtchResidentSwitch.on ? true : false
distination.clientNpForm._ltch = hospitalUniqId
distination.editNpFormMode = editNpFormMode
distination.selectedNpForm = selectedNpForm
}
}
if (segue.identifier == "sendNpVisitToVisitDetailSegue"){
print("test segue fired ")
}
}
//MARK: - #IBOutlet
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var brnTextFieled: UITextField!
#IBOutlet weak var npVisitViewTable: UITableView!
#IBOutlet weak var firstNameErrorLabel: UILabel!
#IBOutlet weak var lastNameErrorLabel: UILabel!
#IBOutlet weak var brnErrorLabel: UILabel!
#IBOutlet weak var isPatientLtchResidentSwitch: UISwitch!
#IBOutlet weak var ltchTextFieled: UITextField!
#IBOutlet weak var saveNpForm: UIButton!
//MARK: -VARIABLES
let validator = Validator()
var pickerLhsc = UIPickerView()
var editNpFormMode = FormMode.Edit
var selectedNpForm = NpForm?()
var npVisits = [NpVisit]()
var moc = DataController().managedObjectContext
var hospitalUniqId:Int = 0
var pickerDataLtch:[(uniqId:Int,hospital:String)] = [(229,"hospital 1"),
(230,"hospital 2"),
(231,"hospital 3")]
}
extension EditNpFormViewController:UIPickerViewDataSource,UIPickerViewDelegate {
// MARK, - Picker View
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
//return pickerDataLhsc.count
return pickerDataLtch.count
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
hospitalUniqId = pickerDataLtch[row].uniqId
ltchTextFieled.text = pickerDataLtch[row].hospital
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)-> String? {
return pickerDataLtch[row].hospital
}
}
extension EditNpFormViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(npVisitViewTable: UITableView, numberOfRowsInSection section: Int) -> Int{
return npVisits.count
}
func tableView(npVisitViewTable: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell:UITableViewCell = npVisitViewTable.dequeueReusableCellWithIdentifier("visitCell")! as UITableViewCell
cell.textLabel?.text = "Visit #\(indexPath.row + 1)"
return cell
}
func tableView(npVisitViewTable: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(npVisitViewTable: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("selected row ")
}
}
In this answer I am making the assumption that didSelectRowAtIndexPath is firing.
In your didSelectRowAtIndexPath, you need to fire the segue that you created from the cell to the second view controller:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("selected row", indexPath)
performSegue(with: "segueIdentifier")
}
I'm not sure what version of Swift you are using, so you might need to make some slight changes.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//self.performSegue(withIdentifier: "sites", sender: self)
self.performSegue(withIdentifier: "CHANGE-TO-YOUT-SEGUE-ID", sender: self)
}
is the "hello" get printed? if so, call your segue from this point. Don't forget to connect your segue (from the top left square) in the story board as manual segue:
If func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) is not called at all, your problem is that the delegate of your UITableView is not set (or not correctly set). The delegate should be your EditNpFormViewController.
You can easily set the delegate of your UITableView instance by simply Control-dragging from the table to the controller that contains it (inside the Storyboard). The other simple way is by setting
tableView.delegate = self
You can just select the table inside the storyboard and drag the delegate from the right panel's circle to your controller:
If your delegate is set right, check the selection attribute of the cell itself:
If your table view is in editing mode, you need to set tableView.allowsSelectionDuringEditing = true

Resources