Label moves to the left when table cell was reused - swift

I have a table with a custom table cell in it. That's how it looks at startup:
Now, when I scroll so that the cell disappears and I let go, the cell comes back to the screen, but it now looks like this:
The table data is fetched using NSFetchedResultsController. I've done exactly this multiple times before, this never happened.
My cellForRowAtmethod:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bitCell", for: indexPath) as! BitCell
let entry = fetchedResultsController.object(at: indexPath)
cell.configure(cellWithText: entry.text!)
return cell
}
My custom BitCell class
class BitCell: UITableViewCell {
#IBOutlet weak var bitTextLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
//selectionStyle = .none
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configure(cellWithText text: String) {
//bitTextLabel.text = text
}
}
And my constraints for the label:
Let me know, if you need anything more (I will edit the question with additional information)
Thanks!

Related

Creating UITableView Cells ONCE Before They are Visible | Swift

Each of my EntryCell: UITableViewCells has an id number that get initialized and saved when the cell is created. My problem is that the cells can get initialized more than once, resulting in different cells being created with different cell ids and different details.
Here is my minimal, reproducible code for the cell initialization:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCellType", for: indexPath) as! MyCellType
cell.textLabel.text = myCellType.id
return cell
}
Here is my minimal, reproducible code for the MyCellType.swift file:
class EntryCell: UITableViewCell {
#IBOutlet weak var textLabel: UILabel!
var id = 0
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
id = UserDefaults.standard.integer(forKey: "nextID")
UserDefaults.standard.set(UserDefaults.standard.integer(forKey: "nextID") + 1, forKey: "nextID")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
When you scroll down far enough that some cells aren't visible, it essentially deletes them ( I think from my research so far ), so when you scroll back up it creates a new, higher id for the new cell.
My question is: is there a way to not have the cells initialize more than once because they are becoming visible multiple times? Or can I have them all initialize at the beginning and never get deleted after that?

How to print .text from a label displayed in a tableView cell?

I have a table view populated through a Nib.
Below is the .xib
And the .swift
import UIKit
class PeopleCell: UITableViewCell {
#IBOutlet weak var peopleCellLabel: UILabel!
#IBOutlet weak var labelTest: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func button(_ sender: UIButton) {
labelTest.text = "Y"
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
In my PeopleDetailsController, how can I retrieve label.text ?
Below code only return "Label" (which is the default value) even if the button was pressed.
extension PeopleDetailsController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifierPeople, for: indexPath) as! PeopleCell
print(cell.labelTest.text)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
}
If you were to do this, you’d retrieve the cell via cellForRow(at:) (not to be confused with the delegate method tableView(_:cellForRowAt:)) which returns the cell associated with an indexPath (assuming that cell is visible):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRowAt(at: indexPath) as? PeopleCell else {
return
}
print(cell.labelTest.text)
}
But this pattern suggests that you’re storing model data within a view, which can be problematic. What if the row scrolled out of view and was discarded or reused? If you scroll it back into view, your string value has been lost.
Your button action handler should trigger the update of the model. Then didSelectRowAt would look up the value in the model, not in the cell.

UITableViewCell not updating UISwitch when selecting row

I have a UITableViewCell that contains a bunch of UISwitches. I want the switch to toggle on/off based on what row the user selects, the data is passing to my cell but the switch state is not updating.
I am using a basic MVC, the Storyboard has a TableView > TableViewCell > Label | UISwitch
Controller:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var testList = [1,2,3,4,5]
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.tableFooterView = UIView()
table.delegate = self
table.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
//Turn them all on at start
cell.setSwitch(rowSelected: true)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
}
SwitchCell
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
#IBOutlet weak var label: UILabel!
func setCell(number: Int){
label.text = String(number)
}
func setSwitch(rowSelected:Bool) {
uiswitch.setOn(rowSelected, animated: true)
}
}
I know I can just make the UISwitch intractable, but I am looking at changing it's state when the user selects the row.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
is incorrect. Using this code you don't pick the cell you want.
You should do simething like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.cellForRow(at: indexPath) as! SwitchCell
cell.setSwitch(rowSelected: false)
}
First of all, we don't dequeue the cell in didSelectRowAt method. Never do that. This will dequeue a brand new cell and won't reflect any changes that you do in it.
So remove the code for tableView(_: didSelectRowAt:) method.
Secondly, you can simply handle the UISwitch state based on cell selection in SwitchCell's definition using setSelected(_:animate:) method like so,
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
uiswitch.setOn(selected, animated: true)
}
//rest of the code...
}
To avoid bulky code, do all the cell layout in custom cell itself instead of doing it in the delegate or dataSource methods. That will only make you ViewController heavier and non-modular.

Selecting row in tableview, removes custom cell

I have a Viewcontroller with a Searchbar at the top with a tableview below. The tableview has a custom cell with 2 labels in it. My problem is that when i run the app and i select a row/cell everything inside the cell disappears. I then force the blank cell outside the visible area of the tableview, so it will be re-used. That's when everything inside the cell is back. Does anyone know why it behaves like this?
My Custom cell class (ContactCell.swift):
import UIKit
class ContactCell: UITableViewCell {
#IBOutlet var lblContactName: UILabel!
#IBOutlet var lblContactTitle: UILabel!
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
}
}
My ViewDidLoad function:
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
My Delegate and Datasource:
extension contactsTabelViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("contactCell", forIndexPath: indexPath) as! ContactCell
if let label = cell.lblContactName{
label.text = "This is a name"
}
if let label3 = cell.lblContactTitle{
label3.text = "This is a title"
}
return ContactCell()
}
}
The problem that caused this problem was that i returned ContactCell() instead of the variable cell
Solution was:
Change this:
return ContactCell()
to this:
return cell
in the cellForRowAtIndexPath function.

Table View with buttons inside of UIViewController

I am trying to use a Table View with cell inside of UIViewController and I want each row to have a button in it. The reason I am using UIViewController instead of UITableView is because I want to have other stuff in that view instead of the whole screen taken by table view.
problem I am having is I only see one button in the last cell. How I can fix this so each row has button in it?
I was hoping that could use something like this
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var logButton: UIButton!
#IBOutlet weak var mytableView: UITableView!
let carLocations = ["Row One", "Row Two", "Row Three"]
override func viewDidLoad() {
super.viewDidLoad()
mytableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return carLocations.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell: UITableViewCell = mytableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
myCell.textLabel?.text = carLocations[indexPath.row]
myCell.detailTextLabel?.text = " Detailed text"
logButton.tag = indexPath.row
// I was hoping that I could use something like this
// myCell.logButton.tag = indexPath.row
return myCell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete) {
// handle delete (by removing the data from your array and updating the tableview)
}
}
}
You can use Custom Cell this way.
Create a new swift file with subclass of UITableViewCell.
Assign that class to your cell by selecting your cell and go to Identity Inspector and it will look a like:
And add elements into your cell which you need for example I have added two labels and one button into cell as per your need and cell will look like:
After that connect outlet of that element into your custom call and your Custom tableview cell class will be:
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var DetailLabel: UILabel!
#IBOutlet weak var btn: UIButton!
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
}
}
Now you can create a custom cell with custom tableview cell class this way in your cellForRowAtIndexPath method:
let myCell = mytableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
And you can assign values to it this way:
myCell.titleLbl.text = carLocations[indexPath.row]
myCell.DetailLabel.text = "Detailed Text"
myCell.btn.tag = indexPath.row
And final code will be:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = mytableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
myCell.titleLbl.text = carLocations[indexPath.row]
myCell.DetailLabel.text = "Detailed Text"
myCell.btn.tag = indexPath.row
return myCell
}
And your result will be:
Check this sample for more Info.
Drop a UITableViewCell on your tableview. That will give you option to customize your cell's look and feel. Create a new class inheriting from UITableViewCell and add that as a class to your tableview cell. Create outlets from cell to this new file and then use cellForRowAtIndexPath to set the properties of the controls inside your cell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! CBTableViewCell
// add self as delegate for tablecell so delegate can call the function defined within
cell.delegate = self
cell.title.text = self.items[indexPath.row]
return cell
}
I would use Custom Cells to solve this problem...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomCell
//Do sth
return cell
}
Your cell:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet var Button: UIButton!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Actually it is very easy: You just drag a UITableView into your view in the size you want. You add a prototype cell to it and then you customise that cell by dragging in labels, etc. You make a new class, which inherits from that UITableViewCell as explained earlier. You also connect the labels and buttons to the class as explained i the other answers. Apple has a very good explanation here Go to the section where they explain how to customise the cell.