I reached a correct value and printed it during the debug sessions. However, when i run the application, the calculated value (newcalory) doesn't show up the specific table cell text field. (aka. cell.itemTotalCalory.text) Do you have any ideas for the solution?
*I attached the related code blocks below.
Thanks a lot,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = ingredientTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! IngredientTableViewCell
cell.ingredientNameTextField.text = ingredients [indexPath.row].ingredientName
cell.numberofItem.text = "1"
let cellcalory = ingredients [indexPath.row].ingredientCalory
cell.itemTotalCalory.text = cellcalory
cell.plusButton.tag = Int(cell.itemTotalCalory.text!)! //indexPath.row
cell.plusButton.addTarget(self, action:#selector(plusAction), for: .touchUpInside)
cell.minusButton.tag = Int(cell.itemTotalCalory.text!)!
cell.minusButton.addTarget(self, action:#selector(minusAction), for: .touchUpInside)
return cell
}
#IBAction func plusAction(sender: UIButton)
{
let cell = ingredientTableView.dequeueReusableCell(withIdentifier: "cell") as! IngredientTableViewCell
let buttonRow = sender.tag
if cell.numberofItem.text == "1" || cell.numberofItem.text != "1"
{
cell.numberofItem.text = "1"
let textValue1 = cell.numberofItem.text
var textValue = Int(textValue1!)
textValue = textValue! + 1
cell.numberofItem.text = String(describing: textValue)
let oldcalory = buttonRow
cell.itemTotalCalory.text = String (((textValue! * Int(oldcalory)) + Int(oldcalory)))
let newcalory = cell.itemTotalCalory.text
refresh(newcalory: newcalory!);
}
}
func refresh(newcalory :String)
{
let cell = ingredientTableView.dequeueReusableCell(withIdentifier: "cell") as! IngredientTableViewCell
cell.itemTotalCalory.text = newcalory
DispatchQueue.main.async {
self.ingredientTableView.reloadData()
}
}
What you should do is to update the value in ingredients array and then call ingredientTableView.reloadData() to reflect this to the UI.
Calling dequeueReusableCell(withIdentifier:) in refresh method will not work as expected for what are you trying to do:
For performance reasons, a table view’s data source should generally
reuse UITableViewCell objects when it assigns cells to rows in its
tableView(_:cellForRowAt:) method. A table view maintains a queue or
list of UITableViewCell objects that the data source has marked for
reuse. Call this method from your data source object when asked to
provide a new cell for the table view. This method dequeues an
existing cell if one is available or creates a new one using the class
or nib file you previously registered. If no cell is available for
reuse and you did not register a class or nib file, this method
returns nil.
So, refresh method should be similar to:
func refresh() {
// updating ingredients array upon reqs satisfaction...
// and then:
ingredientTableView.reloadData()
// nameOfYourRefreshControl.endRefreshing()
}
Also, if you are pretty sure that you want to get a specific cell from the tableView, you might want to use cellForRow(at:) instance method:
Returns the table cell at the specified index path.
func refresh() {
let cell = ingredientTableView?.cellForRow(at: YOUR_INDEX_PATH)
//...
}
Hope this helped.
I found the solution, the lines that are listed below are useless.
let cell = ingredientTableView.dequeueReusableCell(withIdentifier: "cell") as! IngredientTableViewCell
cell.itemTotalCalory.text = newcalory
I updated the ingredient array with the new value inside the plusAction function and my problem solved. Thanks for all postings.
Related
I have a UITableView that has sections (Category0, Category1,..), and every row of a specific section is a UITableView that has one section which is the question (Question1,..) and rows which are the options to be answered (option1, option2,..).
The problem is when I click on a button in a specific category and a specific question (Category0, question1, option0) see screenshot1,
immediately another buttons in another categories are clicked (Category1, question2, option0) see screenshot2,
and (Category4, question1, option0) see screenshot3.
the code below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? insideTableViewCell
cell?.answerlabel.text = "option \(indexPath.row)"
cell?.initCellItem(id: (myObject?.id)! , answer: (myObject?.answerArray![indexPath.row] as? String)!)
return cell!
}
In a custom UITableViewCell which is insideTableViewCell:
func initCellItem(id: Int , answer: String) {
radioButton.setImage( imageLiteral(resourceName: "unchecked"), for: .normal)
radioButton.setImage( imageLiteral(resourceName: "checked"), for: .selected)
radioButton.tag = id
radioButton.setTitle(answer, for: UIControlState.disabled)
radioButton.addTarget(self, action: #selector(self.radioButtonTapped), for: .touchUpInside)
}
#objc func radioButtonTapped(_ radioButton: UIButton) {
print(radioButton.tag)
print(radioButton.title(for: UIControlState.disabled) as Any)
let answer = radioButton.title(for: UIControlState.disabled) as Any
let StrId = String(radioButton.tag)
defaults.set(answer, forKey: StrId)
let isSelected = !self.radioButton.isSelected
self.radioButton.isSelected = isSelected
if isSelected {
deselectOtherButton()
}
}
func deselectOtherButton() {
let tableView = self.superview as! UITableView
let tappedCellIndexPath = tableView.indexPath(for: self)!
let section = tappedCellIndexPath.section
let rowCounts = tableView.numberOfRows(inSection: section)
for row in 0..<rowCounts {
if row != tappedCellIndexPath.row {
let cell = tableView.cellForRow(at: IndexPath(row: row, section: section)) as! insideTableViewCell
cell.radioButton.isSelected = false
}
}
}
You haven't posted code still guessing.
You can create model object like
class QuestionData {
var strQuestion:String? // This may contains Question
var strOptions:[String]? // It may contains options titles of your buttons
var selectedAnswerIndex:Int? // When any button tapped
}
And you should create category models like
class Categories {
var categoryTitle:String?
var questions:[QuestionData] = []
}
you can use this Categories class as main source of your dataSource array
var arrayDataSource = [Categories]()
And fill this with your original data.
now whenever any button tapped you can use selectedAnswerIndex:Int to store current selected option for question. and if it is null then user has not selected any option yet.
I have created class so it is reference type you can directly set the value without worry
Hope it is helpful to you
There has some and simple code I think it will help you :- if it is not sutable for you pls don't mind :-
if (!btnGreen3.isSelected)
{
btnGreen3.isSelected = !btnGreen3.isSelected
}
btnBlue3.isSelected = false
btnBlack3.isSelected = false
You need to save the states of every cell.
The reason is you are using dequereuseable cell with identifier when you scroll it switch to another cell.
So make Array or Dictionary where save the state of every selected and unselected Rows.
My TableView features custom Cells which have a button to display corresponding detailed info in another view.
This thread here got me started and I tried to implement the approach with the delegate inside the customCell:
How to access the content of a custom cell in swift using button tag?
What I want to achieve is that when I click on the button it reads the name of the cell and passes it on to the next controller. However it seems that I cannot pass the name with the delegate method and its field is nil.
How can I get the specific content of a cell when clicking on its button?
This is what I did so far:
In the class creating my own cell I set delegate:
protocol CustomCellDelegate {
func cellButtonTapped(cell: DemoCell)
}
(........)
var delegate: CustomCellDelegate?
#IBAction func buttonTapped(sender: AnyObject) {
delegate?.cellButtonTapped(self)
}
In the TableViewController I have the following:
override func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
let cell =
tableView.dequeueReusableCellWithIdentifier("FoldingCell",
forIndexPath: indexPath) as! DemoCell
cell.delegate = self
//TODO: set all custom cell properties here (retrieve JSON and set in cell), use indexPath.row as arraypointer
let resultList = self.items["result"] as! [[String: AnyObject]]
let itemForThisRow = resultList[indexPath.row]
cell.schoolNameClosedCell.text = itemForThisRow["name"] as! String
cell.schoolNameOpenedCell.text = itemForThisRow["name"] as! String
self.schoolIdHelperField = itemForThisRow["name"] as! String
cell.schoolIntroText.text = itemForThisRow["name"] as! String
//call method when button inside cell is tapped
cell.innerCellButton.addTarget(self, action: #selector(MainTableViewController.cellButtonTapped(_:)), forControlEvents: .TouchUpInside)
cell.school_id = itemForThisRow["name"] as! String
// cell.schoolIntroText.text = "We from xx University..."
return cell
}
And finally the target method when the button inside the cell is clicked
func cellButtonTapped(cell: DemoCell) {
print("the school id: ")
print(cell.schoolNameOpenedCell) //this line throws an error EXC_BAD_ACCESS 0x0
}
Firstly, the object innerCellButton is not a Cell, it's a button. The simple way to solve your problem is, just refer the index of the button. Please find the below method.
func cellButtonTapped(AnyObject: sender) {
let resultList = self.items["result"] as! [[String: AnyObject]]
//Get the tag value of the selected button.
//Button tag should be matching with the corresponding cell's indexpath.row
let selectedIndex = sender.tag
let itemForThisRow = resultList[selectedIndex]
print("the school id: \(itemForThisRow[\"name\"])")
}
* And set each button's tag as indexPath.row *
E.g.,
override func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
// Dequeue your cell and other code goes here.
// set the button's tag like below.
cell.innerCellButton.tag = indexPath.row
return cell
}
Close. I wouldn't use Suresh's method since it does not help find the IndexPath, which includes section and row.
First, I would recommend a model object for your table view data source. Learn more about the MVC pattern as well as parsing a JSON response to an object with mapping. However, this would give you the data you want.
func cellButtonTapped(cell: UITableViewCell) {
let indexPath = tableView.indexPathForCell(cell)
let resultList = self.items["result"] as! [[String: AnyObject]]
let itemForThisRow = resultList[indexPath.row]
let name = itemForThisRow["name"] as! String
}
I'm a Swift newbie and struggling to do something pretty simple.
I want to change the class of a tableViewCell when its tapped. After a lot of Googling I'm now trying to do this by setting a boolean flag (in a dict) and checking the value to determine which class to use.
I've come unstuck with Swift basics of trying to set a variable inside an if statement:
// I think I need to instantiate the cell variable here to be used inside
// and after the if statement but don't know what class type to use.
// I've tried lots of "var cell: xxx = yyy" variations but no luck
if selectedRows[indexPath.row] == true {
let cell = tableView.dequeueReusableCellWithIdentifier("Tier3CellExpanded", forIndexPath: indexPath) as! Tier3CellExpanded
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("Tier3Cell", forIndexPath: indexPath) as! Tier3TableViewCell
}
let image = UIImage(named: entry.thumbnail)
cell.thumbImageView.image = image
cell.busNameLabel.text = entry.busName
cell.busAddressLabel.text = entry.address
return cell
If anyone could point me in the right direction that would be great.
I'm just expanding on Charles A. 's answer to show you how to declare cell outside the if statement but still use 2 different cell types.
//All shared properities would belong to this class
var cell: MySuperclassCellsInheritFrom
if selectedRows[indexPath.row] {
cell = tableView.dequeueReusableCellWithIdentifier("Tier3CellExpanded", forIndexPath: indexPath) as! Tier3CellExpanded
if let expandedCell = cell as? Tier3CellExpanded {
//Set properties specific to Tier3CellExpanded
}
}
else {
cell = tableView.dequeueReusableCellWithIdentifier("Tier3Cell", forIndexPath: indexPath) as! Tier3TableViewCell
if let regularCell = cell as? Tier3TableViewCell {
//Set properties specific to Tier3TableViewCell
}
}
// Configure cell
// Properties that both subclasses share can be set here
return cell
This is possible since we declared cell as UITableViewCell and then cast it after dequeing with identifiers. The cast is possible because the cells you are dequeuing are subclasses of UITableViewCell. So after casting you can now set all of that subclasses individual properties.
This method is useful in case there is other code that you want to apply to both cells that you won't need to duplicate in each if statement such as backgroundColor changes or other base UITableViewCell properties.
You can try like this
if selectedRows[indexPath.row] == true {
let cell = tableView.dequeueReusableCellWithIdentifier("Tier3CellExpanded", forIndexPath: indexPath) as! Tier3CellExpanded
let image = UIImage(named: entry.thumbnail)
cell.thumbImageView.image = image
cell.busNameLabel.text = entry.busName
cell.busAddressLabel.text = entry.address
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("Tier3Cell", forIndexPath: indexPath) as! Tier3TableViewCell
let image = UIImage(named: entry.thumbnail)
cell.thumbImageView.image = image
cell.busNameLabel.text = entry.busName
cell.busAddressLabel.text = entry.address
return cell
}
In your code you are declaring a constant in the if block and another in the else block (that's what the let keyword does), so those will go out of scope immediately after you've set them. Do you have another variable outside your if statement called cell?
I would expect the code to look something like:
let cell: SomeCellType
if selectedRows[indexPath.row] {
cell = ...
}
else {
cell = ...
}
// Configure cell
return cell
The problem I'm trying to solve is to return more than one cell for the loaded task, since i cannot iterate without getting a brand new task loaded.
Each view has one or more cell that is supposed to be loaded for that specific task and i can't seem to solve the problem. Thank you for helping out!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let categoryCells = self.tasks[indexPath.row] as? Task
var cellChooser: BoredTaskCell!
if selectedTaskCategory == "Movie"{
let cell = tableView.dequeueReusableCellWithIdentifier("imdbCell") as! BoredTaskCell!
cell!.selectionStyle = UITableViewCellSelectionStyle.None
cellChooser = cell
}
else if selectedTaskCategory == "Lifestyle" {
let emailCell = tableView.dequeueReusableCellWithIdentifier("emailCreatorCell") as! BoredTaskCell!
emailCell!.selectionStyle = UITableViewCellSelectionStyle.None
cellChooser = emailCell
}
else {
let emptyCell = tableView.dequeueReusableCellWithIdentifier("messageCell") as! BoredTaskCell!
cellChooser = emptyCell
emptyCell!.selectionStyle = UITableViewCellSelectionStyle.None
}
return cellChooser
}
The short answer is that you cannot return two cells from one call to cellForRowAtIndexPath. But to achieve what you want, change the way you construct the tableView, so it has one section for each task, rather than one cell for each task. ie use indexPath.section as the index for your array. You will need to amend all the table view data source methods (eg. so numberOfSectionsInTableView will return self.tasks.count). The trick comes in numberOfRowsInSection, which should typically return 1, but for those tasks where you want two cells, it should return 2. Then in cellForRowAtIndexPath, you determine which task to display using indexPath.section. Then if indexPath.row is 0 use one cell type (eg imdbcell), and if it's 1 use the other cell type (eg message cell).
So, I have got a tableView which shows courses. The user is able to set Checkmarks on these courses (cells) and save them in his PFUser object as a relation to the Courses class (where all courses are stored).
My question is, how do I checkmark the courses a user has already saved at some point before.
This is my attempt, but I don’t know how to continue. How do I get the cells with a specific Label? (Or is there a better way?)
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
let qObjects :Array = query!.findObjects()!
println(qObjects)
for var qObjectsCount = qObjects.count; qObjectsCount > 0; --qObjectsCount {
var qAnObject: AnyObject = qObjects[qObjectsCount - 1]
var courseName = qAnObject["coursename"]
println(courseName)
if let cell: AnyObject? = tableView.dequeueReusableCellWithIdentifier("courseCell"){
}
}
EDIT: that code is in my override viewDidLoad
EDIT2:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("courseCell") as! PFTableViewCell!
if cell == nil {
cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "courseCell")
}
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
let qObjects :Array = query!.findObjects()!
// Extract values from the PFObject to display in the table cell
if let courseName = object?["coursename"] as? String {
cell?.textLabel?.text = courseName
if contains(qObjects, object) {
cell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
}
return cell
}
Error in line ‚if contains(qObjects, object) {'
Generic parameter 'S.Generator.Element’ cannot be bound to non-#objc protocol type 'AnyObject'
EDIT3:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("courseCell") as! PFTableViewCell!
if cell == nil {
cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "courseCell")
}
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
let qObjects :Array = query!.findObjects()!
// Extract values from the PFObject to display in the table cell
if let courseName = object?["coursename"] as? String {
cell?.textLabel?.text = courseName
cell.tintColor = UIColor.blackColor()
}
if contains(qObjects, { $0 === object }) {
cell?.accessoryType = UITableViewCellAccessoryType.Checkmark
self.selectedRows.addIndex(indexPath.row)
}else{
cell?.accessoryType = UITableViewCellAccessoryType.None
}
return cell
}
EDIT4: (Working code)
In the class:
// Initializing qObject variable
var qObjects :Array<AnyObject> = []
In my objectsDidLoad:
// Get PFObjects for the checkmarks on courses the currentUser has already selected before
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
qObjects = query!.findObjects()!
In my tableView(cellForRowAtIndexPath):
// Extract values from the PFObject to display in the table cell
if contains(qObjects, { $0 === object }) {
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
self.selectedRows.addIndex(indexPath.row)
} else {
cell.accessoryType = UITableViewCellAccessoryType.None
}
Don't try to search for a cell. Well, you can, but not like you're trying to - I'll come back to that.
Your current code is creating new cells, not finding existing cells, so that won't work.
What you should really be doing is storing the array of returned objects, qObjects, and then when you're configuring the cell for display checking if that array contains the object for the current cell. If it does, tick it, otherwise remove the tick.
Now, if the load of qObjects happens after the view is shown you have 2 options:
reload the table view
update just the visible items
Option 2. is obviously better, especially if the user might be scrolling the list. To do that you want to use the array returned by calling indexPathsForVisibleRows on the table view. Then, iterate that list, get the associated object and check if it's in qObjects, then get the cell on display with cellForRowAtIndexPath: and update it.