First and foremost, I apologize if my title is kind of misleading compared to my question; I wasn't really sure how to word it out. Hopefully my pictures do more explanation than me. Im still at the beginning stages of iOS development and I seem to have run into my first problem. Basically, I'm creating an app where a person can input item's they have purchased and then later on sold and they can keep track of their profits/losses.
Basically, a user can add an item like the image below and it will then proceed to populate the tableview with the item title, how much they gained or lost through that transaction, and other pieces of information about that item
Pic 1
Second, I have a feature where a user can delete an item from the cell by swiping left. My first problem is that the quantity (I.e 3) and total amount ("$169.82") labels don't update instantly after the deletion of the cell. My second problem is the total amount label itself; I'm able to update the quantity label by simply retrieving the count of the array where the Items Objects are stored in but I'm unable to do so with the total amount label
Pic 2
Pic 3
Here is a snippet of my code
import UIKit
var ClothesList = [String]()
var ClothesResults = [String]()
var ClothesTotalQty = AccessoryList.count
var ClothesBuy = [String]()
var ClothesSell = [String]()
var ClothesFeeArray = [String]()
var ClothesSize = [String]()
var ClothesTotalAmount = 0.0
class ViewClothes: UIViewController, UITableViewDelegate,
UITableViewDataSource {
// MARK: Outlets
#IBOutlet weak var ClothesQuantity: UILabel!
#IBOutlet weak var ClothesAmount: UILabel!
#IBOutlet weak var ClothesNames: UITableView!
// MARK: Functions
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return ClothesList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let list = ClothesNames.dequeueReusableCell(withIdentifier:
"Clothes") as! CustomCells
list.NameLabel?.text = ClothesList[indexPath.row]
list.BuyPriceLabel?.text = "Buy Price: $\
(ClothesBuy[indexPath.row])"
list.FeeLabel?.text = "Fee: \(ClothesFeeArray[indexPath.row])%"
list.SizeLabel?.text = "Size: \(ClothesSize[indexPath.row])"
list.SellLabel?.text = "Sell Price: $\
(ClothesSell[indexPath.row])"
list.ModifiedProfitLabel?.text = ClothesResults[indexPath.row]
return list
}
func tableView(_ tableView: UITableView, commit editingStyle:
UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if(editingStyle==UITableViewCellEditingStyle.delete){
ClothesList.remove(at: indexPath.row)
And here is my attempt at the solution:
/* My Attempt at subtracting the removed cell from the total
amount
let placeholder = ClothesResults[indexPath.row]
ClothesTotalAmount = ClothesTotalAmount - Double(placeholder)!
ClothesResults.remove(at: indexPath.row) */
ClothesTotalQty = ClothesList.count
ClothesNames.reloadData()
}
}
Rest of the code
override func viewDidAppear(_ animated: Bool) {
ClothesNames.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.shadowImage = UIImage()
ClothesNames.delegate = self
ClothesNames.dataSource = self
ClothesQuantity.text = String(ClothesTotalQty)
let totalAmount = ClothesTotalAmount as NSNumber
let totalString = currencyFormatter.string(from: totalAmount)
ClothesAmount.text = totalString
As #Kamran said . You need to recalculate after deleting cell
func tableView(_ tableView: UITableView, commit editingStyle:
UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if(editingStyle==UITableViewCellEditingStyle.delete){
ClothesList.remove(at: indexPath.row)
ClothesQuantity.text = String(ClothesTotalQty)
let totalAmount = ClothesTotalAmount as NSNumber
let totalString = currencyFormatter.string(from: totalAmount)
ClothesAmount.text = totalString
ClothesTotalQty = ClothesList.count
ClothesNames.reloadData()
}
}
simply create a method. In which you have to calculate the total amount and count of clothes from your manged array . And call the function each time you modify the list.
if(editingStyle==UITableViewCellEditingStyle.delete){ RECALCULATE YOUR AMOUNT AND CLOTHES COUNT HERE OR CALL A FUNCTION FOR SAME ClothesNames.reloadData() } }
Related
I have successfully been able to pass the label data of the ItemsVC to the Fees VC when pressing a button in the footer cell
but I can't seem to get it to work when trying to pass the label for the section in the tableview to the FeesVC
Im trying to pass the subtotal from the ItemsVC footer to the FeesViewController labels from different sections instead of it only passing the data of one section
I have successfully passed the data from the footer (when button is pressed). which successfully has been able to pass the totalPriceLbl from the BrandFooter to the Fees VC but can't pass the data from the right section to the FeesVC if that makes any sense how would I be able to pass the correct label data to the fees when the button is pressed
I feel like im really close to my solution im just off somewhere
and I think the problem is in the viewForFooterInSection that passes the subtotal(value) from the section to the FeesVC
thanks in advance for any help provided
class ItemsViewController: UIViewController {
var brandItems: [BrandItem] = []
var groupedBrandItems: [String: [BrandItem]] = [:]
var brandSectionTitle: [String] = []
var selectedLabel: String? // Populates label data in FeesVC
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
groupedBrandItems = Dictionary(grouping: brandItems, by: {$0.products.brandName})
brandSectionTitle = groupedBrandItems.map{$0.key}.sorted()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}if let vc = segue.destination as? FeesViewController {
vc.stringPassed = selectedLabel! // Populates label data in FeesVC
}
}
}
extension ItemsViewController: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return brandSectionTitle.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let brand = brandSectionTitle[section]
return groupedBrandItems[brand]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let brandCell = tableView.dequeueReusableCell(withIdentifier: "BrandCell") as! BrandCell
let brand = brandSectionTitle[indexPath.section]
let brandItemsToDisplay = groupedBrandItems[brand]![indexPath.row]
brandCell.configure(withCartItems: brandItemsToDisplay.products)
return brandCell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let brandHeader = tableView.dequeueReusableCell(withIdentifier: "BrandHeader") as! BrandHeader
let headerTitle = brandSectionTitle[section]
brandHeader.brandName.text = "Brand: \(headerTitle)"
return brandHeader
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let brandFooter = tableView.dequeueReusableCell(withIdentifier: "BrandFooter") as! BrandFooter
let brand = brandSectionTitle[section]
let arrAllItems = groupedCartItems[brand]!
var subtotal: Float = 0
for item in arrAllItems {
if item.products.selectedOption == 1 {
subtotal = subtotal + (Float(item.products.price) * Float(item.products.count))
}
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
let total = numberFormatter.string(from: NSNumber(value: Float(subtotal)))
brandFooter.totalPrice.text = String(Total!)
//trying to pass each section subtotal to FeesVC
self.selectedLabel = "\(subtotal)" // passes code to FeesVC
return brandFooter
}
}
class FeesViewController: UIViewController {
#IBOutlet weak var feesView: UIView!
#IBOutlet weak var subtotalLbl: UILabel!
#IBOutlet weak var salesTaxLbl: UILabel!
#IBOutlet weak var totalLbl: UILabel!
var stringPassed = String() // Populates label data in FeesVC
override func viewDidLoad() {
super.viewDidLoad()
let tax = Float(stringPassed)! * Float(0.0825)
let total = Float(stringPassed)! + tax
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
let subTotal = numberFormatter.string(from: NSNumber(value: Float(stringPassed)!))
let salesTax = numberFormatter.string(from: NSNumber(value: Float(tax)))
let overallTotal = numberFormatter.string(from: NSNumber(value: Float(total)))
subtotalLbl.text = subTotal
salesTaxLbl.text = "(\(String(describing: salesTax!)))"
totalLbl.text = "(\(String(describing: overallTotal!)))"
}
#IBAction func returnButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
print("Close Taxes and Fees")
}
}
Update :
I keep getting the same subtotal and calculations in the FeesVC when im trying to post the subtotal for each section whenever the moreinfo button is pressed
self.selectedLabel = "\(subtotal)"
this line of code in viewForFooterInSection works successfully in passing the subtotal when the button is pressed for one section but it just doesn't pass the subtotal for every section when the button is pressed in their appropriate footer
first declare an array of doubles to store each one of your subtotals.
var subTotalArray[Double] = [Double]()
Then, when you are calculating your subtotals inside your viewForFooterInSection function, initialize your created array like so (change according to your code & variable names):
subTotalArray[section] = subtotal
// use the local variable "section" to iterate
// and assign the subtotal to the correct section footer
Next to determine which info button was pressed in order to get the correct subtotal data, you can configure the tag property of the moreInfo button inside your viewForFooterInSection function. Assign the tag like so:
moreInfo.tag = section
Finally with all these variables in place, we can correctly assign, determine, and gather each individual subtotal with a handler function connected to your moreInfo button.
func handleMoreInfo(sender: UIButton) {
selectedLabel = subTotalArray[sender.tag]
}
Assuming what I understood from your problem you need to pass subtotals to other VC.
what you're doing wrong is saving the subtotal in one variable in footerForSection , this var will have the last subtotal of last footer section(this is what is happening in your code)
self.selectedLabel = "\(subtotal)" // passes code to FeesVC
EDIT
So in your scenario you need to save subtotals in a array and when you tap a section footer , at the tap get the index of section and use the array to get the data and pass it on to other VC
I searched around for pagination but everything seems to point to pulling data from a server:
Pagination
Pagination
In my situation my data arrays are filled with static items wherein as I already know the count of what's inside of each array. My tabBar has three tabs and each tab has a tableVIew with different amounts of data
tableDataForTabOne = [DataModel]() // the array has 1000 items in it
tableDataForTabTwo = [DataModel]() // the array has 690 items in it
tableDataForTabThree = [DataModel]() // the array has 7 items in it
How do I paginate the arrays for the tableView into different pages? For example the first 10 items is 1 page, the next 10 items is another page, etc etc?
The question has nothing to do with the tabs. I don't know how to paginate on a tableView without pulling data from a server.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak fileprivate var tableView: UITableView!
let tableDataForTabTwo = [DataModel]() //has 690 items in it
var pageNumber = 0
override func viewDidLoad() {
super.viewDidLoad()
...
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableDataForTabTwo.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
cell.titleLabel.text = tableDataForTabTwo[indexPath.row]."some static title"
cell.imageView.image = tableDataForTabTwo[indexPath.row].UIImage(named: "some static image")
return cell
}
}
Before anyone suggests to pull the Firebase data from within the PlayerController's viewWillAppear, I already know how to do that and if I did it that way I know how to pass the data to the ScoreController. In this situation I need to pull the data directly from within the cell and somehow pass the data back from there.
I have a tableView inside a PlayerController that displays the randomPower, name, and score of each player. Inside the tableView's cell I pull the name and score from Firebase using a function getScoreDataFromFirebase(). The function is located directly inside the tableView's PlayerCell and once I get the values from Firebase I initialize the cell's name and score outlets right then and there.
Inside the tableView's cellForRowAtIndexPath I call cell.getScoreDataFromFirebase() and everything works fine because both outlets display the correct values.
From that point on I have a ScoreController. When a tableView cell is chosen the score is sent to the ScoreController.
The problem is since I'm pulling the data directly from within the cell itself the only way I could pass the score (pulled from Firebase) to ScoreController was to 1st set a didSet score property inside the cell.
Still inside the cell when I pull the score data from Firebase 2nd I initialize the score property with it
3rd inside the tableView's cellForAtIndexPath I use an if let to pass the value from the cell's score property to the the tableData.
When I first try to send the indexPath of that tableData over to the ScoreController sometimes it's nil even though the cell's score property definitely has a value (I used to break points to check). If I select any of the very first few tableView cells that are visible they will have a nil value for the score property. However if I scroll further down through the cells and back up then those same cells will no longer have a nil score property.
What I found out was the if let statement was running before the Firebase code was pulled so the score property was nil for first few cells that are on scene. The odd thing is everything works fine once I start scrolling.
How can I pass a value pulled directly from a cell to the tableView's didSelectRow?
PlayerModel:
class PlayerModel{
name:String?
score:String?
randomPower:String?
}
TableViewCell SubClass:
class PlayerCell:UITableViewCell{
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var randomPowerLabel: UILabel!
internal var score: String?{
didSet{
print("**********\(score ?? "*********")")
}
}
override func prepareForReuse() {
super.prepareForReuse()
nameLabel.text = " "
scoreLabel.text = " "
}
func getScoreDataFromFirebase(){
let scoreRef = usersRef?.child("score")
scoreRef?.observe( .value, with: {
(snapshot) in
for child in snapshot.children{
let user = child as! DataSnapshot
for player in user.children{
let eachPlayer = player as! DataSnapshot
if let dict = eachPlayer.value as? [String:Any]{
let name = dict["name"] as? String ?? " "
let score = dict["score"] as? String ?? " "
self.nameLabel.text = name
self.scoreLabel.text = score
self.score = score
}
}
}
}
}
}
TableView:
class PlayerController: UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak fileprivate var tableView: UITableView!
var players = [PlayerModel]() // this array is populated with data from a previous vc. The number of players in the array are the same exact number of players that's pulled from the getScoreDataFromFirebase() function
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return players.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath) as! PlayerCell
let cellData = players[indexPath.row]
cellData.randomPowerLabel.text = cellData.power
cell.getScoreDataFromFirebase()
if let score = cell.score{
cellData.score = score
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let indexPath = tableView.indexPathForSelectedRow else { return }
let scoreVC = storyboard?.instantiateViewController(withIdentifier: "ScoreController") as! ScoreController
scoreVC.score = players[indexPath.row].score
}
You can achieve this using delegation :
Create a protocol
protocol UpdateValueDelegate: class {
func changeValue(score: String, row: Int)
}
Your UIViewController should look like this :
PlayController : UITableViewDataSource, UITableViewDelegate, UpdateValueDelegate
{
var scoreDict:[String:String] = [:]
//
//
func changeValue(score: String, row: Int)
{
self.scoreDict["\(row)"] = score
}
}
In cellForRowAtIndexPath set cell.delegate = self and cell.row = indexPath.row
Your UITableViewCell should look like this :
class PlayerCell:UITableViewCell: UITableViewCell
{
weak var delegate: UpdateValueDelegate?
var row: Int?
//
//
}
Finally pass score from getScoreDataFromFirebase by calling delegate function:
func getScoreDataFromFirebase()
{
//
//
delegate. changeValue(score: localScore, row: self.row)
}
Now you have the value in your viewController from where it can be easily passed to didSelectRow using the global dictionary ** scoreDict**.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
var score = self.scoreDict["\(indexPath.row)"]
// Use score
}
I guess this could be one of my rookie mistakes I couldn't figure out.
I have an app which has a table view. It has text label and detail text label.
When I select a row, I takes me to another story board using segue...all of this works fine except the table view display on my simulator.
detail text label shows up on the simulator shown in this picture circled.
Here is the code I am using to detect cell/row selected. When I comment it out this issue goes away...
What you see in the red circle is gradeselected which is also in the detail text label in the tableview.
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
let gradeselected = String(describing: sgrade)
return [gradeselected]
}
Screenshot of simulator with the issue
Please help in resolving this issue. Let me know if you need any more info.
Xcode 9.1
Swift 4
#Caleb here is my code.
import UIKit
class StudentsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var cellButton: UIButton!
#IBOutlet weak var studentDetailTable: UITableView!
var sname:[String]?
var sgrade:[Int]?
var gradetext = "Grade:"
var sstudentname = ""
override func viewDidLoad() {
super.viewDidLoad()
studentDetailTable.delegate = self
studentDetailTable.dataSource = self
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sname!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = studentDetailTable.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = sname[indexPath.row] + gradetext + String(sgrade[indexPath.row])
sstudentname = sname![indexPath.row]
cell?.detailTextLabel?.text = String(sgrade![indexPath.row])
cell?.layer.cornerRadius = (cell?.frame.height)!/2
cell?.backgroundColor = UIColor.blue
cell?.textLabel?.textColor = UIColor.white
cell?.layer.borderWidth = 6.0
cell?.layer.cornerRadius = 15
cell?.layer.borderColor = UIColor.white.cgColor
cell?.textLabel?.textColor = UIColor.white
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedIndex = tableView.dataSource?.sectionIndexTitles!(for: studentDetailTable)
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!)!
let scell = currentCell.detailTextLabel!.text!
sstudentname = (currentCell.textLabel?.text)!
}
// - If I comment this section of the code issue goes away.
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
let gradeselected = String(describing: sgrade)
return [gradeselected]
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let myKLVC = segue.destination as! KindergartenLevelViewController
myKLVC.klvstudentname = sstudentname
}
The text in the red circle says [1, 2], which looks like the array that probably holds all the grades, not just the one for a specific cell that we see in the string gradeselected. If you have such an array in your code, look for places where you might be converting it to a string and drawing it. Maybe you did that in an earlier iteration of your code to make sure that the array contained what you thought, or something?
Arrays don't just mysteriously draw themselves on the screen — somewhere, there's some code that causes that to happen. We can't really help you find it because you haven't shown very much of your code, but just knowing what to look for may help you find it yourself.
You can query the selected row via table view's property indexPathForSelectedRow.
The method you have implemented does exactly what you see in the simulator.
Just have a look at the documentation:
property indexPathForSelectedRow: https://developer.apple.com/documentation/uikit/uitableview/1615000-indexpathforselectedrow
func sectionIndexTitles: https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614857-sectionindextitles
I would like to show all the values of this loop in the tableview. The code is to calculate an Amortization Table for loans. I tried saving the data of the loop in the array, but it always gives me the last values. I really got stuck on this. So how can I do that, please? This is my code:
import UIKit
class tableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var arr = [Int]()
var cell:tableCell!
var TPayment: float_t! // calls value of 59600 from main controller
var years1: float_t! // number of months = 180 ( 15 years)
var monthlyPayment: float_t! // 471
var interest: float_t! // 5%
var principil: float_t! //222
var interestrate: float_t! // 249
var initil: float_t!
var data = Array<float_t>()
var data2: NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
let c = Int(years1)
arr += 0...c
tableCalculation()
// Register custom cell
let nib = UINib(nibName: "table", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
}
func tableCalculation() {
let years = Int(years1)
initil = TPayment - 0
for i in 0..<years {
initil = initil - principil
interest = initil * interestrate
principil = monthlyPayment - interest
print("Month : \(monthlyPayment), principil: \(principil),interest: \(interest), initi: \(initil)")
data = [interest]
self.data2 = [initil]
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! tableCell
cell.lbl1.text = "\(arr[indexPath.row])"
cell.lbl2.text = "\(monthlyPayment)"
cell.lbl3.text = "\(data[indexPath.row % data.count])"
cell.lbl4.text = "\(principal)"
cell.lbl5.text = "\(self.data2[indexPath.section])"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Row \(indexPath.row) selected")
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 40
}
}
Table View with UITableView
Table view with print()
The main issue is with your data array.
In your loop where you populate your data array, in tableCalculation, there's this:
data = [interest]
It means that for each iteration you set the data array to [interest] instead of appending the new item to the array.
What you should do instead:
data.append(interest)
Note that you make the same mistake with self.data2. But now you know how to fix this kind of error.
In the cellForRowAtIndexPath method you are giving the same data to print that looks like the issue
I don't get much for all the labels but you can change it based on the requirement
for lbl2 and lbl4 you are passing a same single float variable for all the rows that's why it show the same value, if you want to show the different value for each row you should store it in array and at cellForRowAtIndexPath get it like you are doing for lbl1
cell.lbl2.text = currencyFormatter(monthlyPayment)
cell.lbl4.text = currencyFormatter(principil)
for lbl5 Your code cell code should be like this
cell.lbl5.text = "\(self.data2[indexPath.row])"
For the lbl 3 & lbl 5 when i execute this code with static value to get interest it only stores one value in the array
for i in 0..<years {
let interest = 5.0 * 4.0
data = [interest]
}
to store every value you calculated in array you should use append
data.append(interest)
self.data2.append(initil)
as there is only 1 value in the array for every index path it gives 0th value in the array as per your modulo operation so it shows same value in each row