Accessing the components in a custom cell? - ios

I have a table using custom cells, within a custom cell i have a picker and a textfield.
I have a custom class for the cell that has outlets for these elements.
In the parent VC of the table I want to be able to reference the contensts of the text field and the value of the picker.
How do i gain access to these values when they are in a custom cell rather than just on the main viewcontroller view?
Code for cell:
class NewExerciseTableViewCell: UITableViewCell {
static let reuseIdentifier = "Cell"
#IBOutlet weak var setNumber: UILabel!
#IBOutlet weak var repsPicker: UIPickerView!
#IBOutlet weak var userExerciseWeight: UITextField!
}
I tried to access it by creating let setCell = NewExerciseTableViewCell() and then trying to access its contents components via its properties, but thats not the way to do it!
Appreciate any assistance here on how I can pull out the values in this cell!
edit: here is my callForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? NewExerciseTableViewCell else {
fatalError("Unexpected Index Path")
}
cell.backgroundColor = UIColor.customBackgroundGraphite()
cell.textLabel?.textColor = UIColor.white
return cell
}

You need to create an object of UITableViewCell with the type of your custom type. Let say you are making a table view then you can add the cells in cellForRowAt. See below code for reference
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NewExerciseTableViewCell
cell.setNumber.text = // Some Value
}
In the above method the cell is now of type NewExerciseTableViewCell and you can now access any public property of that class.
If you want to get the values of particular cell then you will need to get the cell first. Check the below code for reference
let cell = yourTableView.cellForRowAtIndexPath(indexPath) as! NewExerciseTableViewCell
print(cell.setNumber.text)

This question contains multiple parts, and I'll try to address them one by one.
1- First of all if you want to set the values, properties, etc. you have to do that in tableView(_:cellForRowAt:) method.
(Set the cellIdentifer in your storyboard.)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIndetifier", for: indexPath) as! NewExerciseTableViewCell
cell.setNumber.text = "1"
...
return cell
}
2- You cannot access cell's subview just by creating a new one. That doesn't make sense. Use let cell = tableView.cellForRow(at: ...) and then you have a valid cell.setNumber
P.S. btw the setNumber is not a good practice for naming a label. Use set only for setter methods.

Related

How do I create an action that changes the properties of a specific uicollectionviewcell?

I have an app with a UICollectionView. Each custom cell has a circular progress view and a button that's supposed to up its progress, but I can't figure out how to change the properties for the specific cell's subviews inside the button action.
I've tried
adding a target to the button inside the "cellForItemAt indexPath" function, but then how to call for that specific cell inside the target's function.
adding a IBAction inside the custom cell class, but same problem again
Basically the problem is how can you call for a specific cell at a certain index path outside the "cellForItemAt" function?
You can setup your UICollectionViewCell subclass something like this:
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var progressView: UIProgressView!
var buttonAction: (() -> Void)?
#IBAction func didPressButton() {
buttonAction?()
}
}
and in your UITableViewDelegate
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "your identifier", for: indexPath) as! MyCollectionViewCell
cell.buttonAction = {
//Do whatever you wish
}
...
...
return cell
}
Adding a target to the button inside the cellForItemAt indexPath function, also send the index path as a tag with the button function like:-
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "your identifier", for: indexPath) as! MyCollectionViewCell
cell.buttonAction = {
//Do whatever you wish
}
...
...
**cell.button.tag = indexPath.row**
return cell
}
Then the function in which you have set as target, you need to update that particular cell to update UI by writing the below code
[self.collectionView reloadItemsAtIndexPaths:#[Sender.tag]];

Swift Custom Tableview Cell with UITableViewCellStyle

I am trying to make a custom table view cell.
If I do this:
class TableViewCell: UITableViewCell {
#IBOutlet weak var cellBackgroundImage : UIImageView!
}
And:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
cell.cellBackgroundImage.backgroundColor = UIColor.white
cell.cellBackgroundImage.layer.masksToBounds = false
cell.cellBackgroundImage.layer.cornerRadius = 5
let event = self.fetchedResultsController.object(at: indexPath)
self.configureCell(cell, withEvent: event)
return cell
}
I obtain a white rounded cell background. Easy. And I can use the original cell.textLabel.text.
Perfect.
But, if I want to do something more complex:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? TableViewCell
if (cell == nil) {
cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "Cell") as? TableViewCell
cell?.cellBackgroundImage.backgroundColor = UIColor.white
cell?.cellBackgroundImage.layer.masksToBounds = false
cell?.cellBackgroundImage.layer.cornerRadius = 5
self.configureCell(cell!, withObject: object)
}
And at the same time to use the original table view properties as UITableViewCellStyle.subtitle and cell.accessoryView, the app crashes or shows the wrong output.
THIS MEANS THAT I MUST USE A FULL CUSTOM CELL WITH MORE OUTLETS TO REPLACE THE ORIGINAL ELEMENTS AS UITableViewCellStyle.subtitle and cell.accessoryView ???
I will express it in a different way:
Can I use a custom tableview cell only for one purpose (like the rounded background) and use the original elements such as the subtitle style and the accesory view?
In afirmative case, how?

Content views are not displayed on the UITableViewCell

This is a part of my storyboard:
this is my running app:
This is my part of codes:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
if indexPath.row == 0 {
return super.tableView(tableView, cellForRowAt: indexPath)
} else {
tableView.register(SubTextFieldCell.self, forCellReuseIdentifier: "SubTextFieldCell")
let cell = tableView.dequeueReusableCell(withIdentifier: "SubTextFieldCell", for: indexPath) as! SubTextFieldCell
// cell.deleteButton.isEnabled = true
// cell.subTextfield.text = "OK"
print("indexPath.row: \(indexPath.row)")
return cell
}
...
I have already connected the button and the textfield in various places and I can guarantee that this part is not wrong, but when I click the Add button in the first row, I only get a cell without any content.
If I use code like this cell.deleteButton..., Xcode will report an error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
Then I tried to use the viewWithTag method to see if show the content, but I still get the same error as before.
This is the first time I have encountered this kind of error. I have no error with similar code and methods in my other programs.
When you configure custom cells inside a storyboard file, you don't need to call register(_:forCellReuseIdentifier:) because the storyboard should have done that for you.
The reason deleteButton is nil is because by re-registering the cell class as you did, you overwrote what the storyboard registered for you. All cells created by dequeueing with that reuse identifier will have no connection to the storyboard and simply be empty.
Assuming all the #IBOutlets and reuse identifiers and things are set up (which you said you did), then simply dequeue the cell with the reuse identifier set up in storyboard.
Dequeue Cell Example:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
if indexPath.row == 0 {
return super.tableView(tableView, cellForRowAt: indexPath)
} else {
// Registering again is unnecessary, because the storyboard should have already done that.
// tableView.register(SubTextFieldCell.self, forCellReuseIdentifier: "SubTextFieldCell")
let cell = tableView.dequeueReusableCell(withIdentifier: "SubTextFieldCell") as! SubTextFieldCell
cell.deleteButton.isEnabled = true
cell.subTextfield.text = "OK"
return cell
}
} else {
...
}
}
Note:
Even in cases where you do need to register a class with a table view, you should only have to do this once. (For example, during viewDidLoad)
Even in those times, you should not call it every time you dequeue a cell. You're just making your app work harder.
Connecting views to cells in Storyboard
Set a subclass to the table view
Set a subclass to the first prototype cell
Set a reuse identifier to the prototype cell
Make sure subview (UIButton, etc.) is connected to property with #IBOutlet (subclass code shown below)
Example UITableViewController subclass:
class MyTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyFirstCell", for: indexPath) as! MyFirstTableViewCell
// Configure cell if needed
cell.myButton.setTitle("New Button Text", for: .normal)
cell.myButton.tintColor = .green
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "MySecondCell", for: indexPath) as! MySecondTableViewCell
// Configure cell if needed
cell.myTextField.backgroundColor = .red
return cell
}
}
}
Example UITableViewCell subclass:
class MyFirstTableViewCell: UITableViewCell {
#IBOutlet weak var myButton: UIButton!
}
Result:

Prototype UITableViewCell with other objects (UITextField, UISwitch)

I'm trying to make a UITableView that can support having different objects/elements inside it. Specifically, these elements are a UITextField and UISwitch.
The first problem:
The elements do not show up. They are placed in the prototype cell, which is then constructed inside the class I have set up. I have verified that the cell setup is working because I can change the words on each cell, but there are no elements within the cell.
Here is the code that constructs my cells right now:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return 1
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "EmailCell")
return cell
}
Second problem (which might be solved along with the first):
I have no way of accessing the information in each UITextField or each UISwitch. How can I get this information from all cells that exist?
Thanks in advance for the help!
There are multiple things wrong with your code.
For custom cells you need to implement a custom UITableViewCell subclass. Here is an example:
import UIKit
class EmailCell: UITableViewCell {
#IBOutlet var customTextField: UITextField!
#IBOutlet var customSwitch: UISwitch!
}
After that, open your Storyboard and select the prototype cell. Change it's class to EmailCell in the Identity Inspector.
Also make sure to connect your ui elements to the #IBOutlets created earlier. See this StackOverflow post if you need help with #IBOutlet.
In the next step, change your tableView(_:, cellForRowAt:) implementation like this:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "EmailCell", for: indexPath) as! EmailCell
// Configure your custom ui elements however you want
cell.customTextField.text = "somestring"
cell.customSwitch.isOn = true
return cell
}
Make sure your cells have reuse identifiers and you're using
tableView.dequeueReusableCell(withIdentifier: -Your cell Id- , for: indexPath) as? -Your Cell Class-
in your cell for row at index datasource method
next you can add targets to your cell text field / switch by doing this in your cell for row at index datasource method
cell.-your switch / text field-.addTarget(self, action: #selector(valueChanged(_:)), for: .valueChanged)
and you should subclass a uitableview cell to add the property / iboutlets
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var yourSwitch: UISwitch!
}

How to find and set the text to UILabel of UITableViewCell in Swift?

I use Http get to get the json data and parse it.
It works fine so far and it shows the data on the UITableView.
I have a UITableCell in UITableView.
And the cell also has three UILabel-s in it like in the following picture.
And I have set the Identifier of TableViewCell to "Cell" like in the following picture.
I want to set "AAA" , "BBB" , "CCC" to the UILebel-s in UITableViewCell, but in the following code, I can not find any UILabel in UITableViewCell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.
return cell
}
Should I need to connect the UILabel to Swift file?
How to find and set the text to UILabel of UITableViewCell?
Did I missing something?
Reason why you can not access the labels in the code is that you have no connection of labels with the code, by adding labels only on the default UITableViewCell just won't work.
I suppose you are using custom UITableViewCell? if not then use a custom cell and add three labels in the code and connect them on UI as outlets and in your code you can access them by using cell.label1 and so on
class CustomTableViewCell: UITableViewCell {
#IBOutlet var label1:UILabel!
#IBOutlet var label2:UILabel!
#IBOutlet var label3:UILabel!
}
In your main tableview class you could access them like as follows
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTableViewCell
cell.label1.text = "AAAA"
return cell
}
Yes, you can do this by giving every label a unique tag and then access that label in the ViewController with the tag property then your cellforRowatIndexpath will change to following code :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? CustomTableViewCell {
if let label1 = cell.viewWithTag(1) as? UILabel { // 1 is tag of first label
label1.text = "AAAA"
}
if let label2 = cell.viewWithTag(2) as? UILabel { // 2 is tag of second label
label2.text = "BBBB"
}
if let label3 = cell.viewWithTag(3) as? UILabel { // 3 is tag of third label
label3.text = "CCCC"
}
return cell
}
return UITableViewCell()
}
Hope it will help you.

Resources