Xcode: Swift - Table Cell not showing all text - ios

My cells are getting the text fine, but they aren't showing all the text.
Image: http://i.imgur.com/Aql1meS.png
Here is the code for my table view controller:
class ResultsTableViewController: UITableViewController {
var mapItems: [MKMapItem] = [MKMapItem]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return mapItems.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("resultCell", forIndexPath: indexPath) as! ResultsTableCell
if(indexPath.row % 2 == 0) {
cell.backgroundColor = UIColor.clearColor()
}else{
cell.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.2)
cell.textLabel?.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.0)
}
// Configure the cell...
let row = indexPath.row
let item = mapItems[row]
cell.nameLabel.text = item.name
cell.phoneLabel.text = item.phoneNumber
return cell
}
}
I've searched around to see if I have a character limit set, but can't seem to find anything. Thanks in advance.

It seems like your UILabels aren't wide enough. If you make them wider, (or apply auto layout on all 4 sides, well... that gets more complicating,) then it should work fine.
Hope this helps!!

Related

expand and contract label not working in UITableViewCell

I am trying to use a UITableView and have cell contents which will expand or contract when the user clicks on the label.
However, the behavior I'm seeing is that the cell will contract (e.g. I am changing the label's numberOfLines from 0 to 1, and then the label will contract). However, when I change the label's numberOfLines from 1 to 0 it doesn't expand.
I put together a simple test program to show the issue I'm having.
I'm using a UITapGestureRecognizer to handle the tap event for the label, and that is where I expand or contract the label. Does anyone have any idea what I'm doing wrong?
Here's my storyboard and view controller code.
import UIKit
class MyCell : UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
}
class TableViewController: UITableViewController {
let cellID = "cell"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 75
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 12
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "section " + String(section)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 4
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: self.cellID, for: indexPath) as! MyCell
cell.myLabel.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleCellTapped(_:)))
cell.myLabel!.addGestureRecognizer(tapGesture)
// Configure the cell...
if indexPath.row % 2 == 0 {
cell.myLabel?.numberOfLines = 1
cell.myLabel.text = "This is some long text that should be truncated. It should not span over multiple lines. Let's hope this actually works. \(indexPath.row)"
} else {
cell.myLabel?.numberOfLines = 0
cell.myLabel.text = "This is some really, really long text. It should span over multiple lines. Let's hope this actually works. \(indexPath.row)"
}
return cell
}
#objc func handleCellTapped(_ sender: UITapGestureRecognizer) {
print("Inside handleCellTapped...")
guard let label = (sender.view as? UILabel) else { return }
//label.translatesAutoresizingMaskIntoConstraints = false
// expand or contract the cell accordingly
if label.numberOfLines == 0 {
label.numberOfLines = 1
} else {
label.numberOfLines = 0
}
}
}
Do two things.
Set the Vertical Content hugging priority and
Vertical Content compression resistance priority of the Label to 1000.
After changing the number of lines of the Label call the tableView.beginUpdates() and tableView.endUpdates()
This should work definitely.
You almost get it, but here is a couple of things you should care about.
First, handle the label by UIGestureRecognizer it's quite overhead. For that purposes UITableViewDelegate has own method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
Second, you're using self-sizing cell, because of
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 75
There is one important rule for that: you should pin myLabel to each side of superview (see official docs why):
Last step, when the numberOfLines changed, you should animate cell's height (expand or collapse) without reloading the cell:
tableView.beginUpdates()
tableView.endUpdates()
Docs:
You can also use this method followed by the endUpdates() method to animate the change in the row heights without reloading the cell.
Full code:
class MyCell: UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
}
class TableViewController: UITableViewController {
let cellID = "cell"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 75
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 12
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "section " + String(section)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: self.cellID, for: indexPath) as! MyCell
cell.selectionStyle = .none // remove if you need cell selection
if indexPath.row % 2 == 0 {
cell.myLabel?.numberOfLines = 1
cell.myLabel.text = "This is some long text that should be truncated. It should not span over multiple lines. Let's hope this actually works. \(indexPath.row)"
} else {
cell.myLabel?.numberOfLines = 0
cell.myLabel.text = "This is some really, really long text. It should span over multiple lines. Let's hope this actually works. \(indexPath.row)"
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
guard let cell = tableView.cellForRow(at: indexPath) as? MyCell else { return }
cell.myLabel.numberOfLines = cell.myLabel.numberOfLines == 0 ? 1 : 0
tableView.beginUpdates()
tableView.endUpdates()
}
}
Try
tableView.beginUpdates()
if label.numberOfLines == 0 {
label.numberOfLines = 1
} else {
label.numberOfLines = 0
}
tableView.endUpdates()

Control multiple tableviews in a single view

I have a table view nested in a collection view and i'm returning 3 (possibly more in the future) collection view cells and I was wondering if it is possible to present different content in each one of the collection cells? I attached a few screenshots to better understand what I am taking about. Thanks.
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 3
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel?.text = "Homeroom"
cell.detailTextLabel?.text = "8:15 AM - 9:00 AM"
cell.selectionStyle = .None
return cell
}
Yes you can. You need set a property for every tableView you have and in delegate method compare it like below
class Some: UIViewController {
var firstTableView: UITableView
var secondTableView: UITableView
override func viewDidLoad() {
firstTableView = YOUR_FIRST
secondTableView = YOUR_Second
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if tableView == firstTableView {
return 2;
}
else if tableView == secondTableView {
return 1;
}
return 3
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if tableView == firstTableView {
return 2;
}
else if tableView == secondTableView {
return 1;
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
if tableView == firstTableView {
cell = tableView.dequeueReusableCellWithIdentifier("cellOfFirstTableView", forIndexPath: indexPath) as! UITableViewCell
}
else if tableView == secondTableView {
cell = tableView.dequeueReusableCellWithIdentifier("cellOfSecondTableView", forIndexPath: indexPath) as! UITableViewCell
}
// Configure the cell...
if tableView == firstTableView {
cell.textLabel?.text = "Homeroom"
cell.detailTextLabel?.text = "8:15 AM - 9:00 AM"
cell.selectionStyle = .None
}
else if tableView == secondTableView {
cell.textLabel?.text = "Homeroom"
cell.detailTextLabel?.text = "8:15 AM - 9:00 AM"
cell.selectionStyle = .None
}
return cell
}
}
You can use UITableViewDelegate / UITableViewDataSource methods with if else conditions or some thing similar
eg.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == table1 { // table1 is a global var for the table
} else if tableView == table2 {
}
}
But I think it will be very clear if you use separate controller class for each table so you can easily manage the code.
But this depends on what type of data you have. If data is completely unrelated you can just use 3 different controllers.
Or if you can reuse data and codes among 3 tables then you can decide if you wanna use 3 different controllers or to use i class with above method.
eg.
let table1Controller = Table1Controller(dataList1)
let table2Controller = Table2Controller(dataList2)
let table3Controller = Table3Controller(dataList3)
table1.delegate = table1Controller
table1.dataSource = table1Controller
table2.delegate = table2Controller
table2.dataSource = table2Controller
table3.delegate = table3Controller
table3.dataSource = table3Controller

UITableView with Two Prototype Cells

UPDATE- Thanks to Farhad for answering my initial post. At the bottom of this post is a new issue i've encountered regarding automatic dimensioning for dynamic cell height
I've tried searching for other posts but to no avail.
As the title suggests I want to have my table view show two cells with the second cell being custom.
The first cell will have 4 rows - O.D, Weight, I.D, Grade.
The second cell, the custom cell, will have 8 labels displaying the results from the inputs from the first cell.
I have a reuse identifier for both cells. The first cell i've called "capacitiesCell" and my second cell I've called "cell2"
For my second cell, I have created a new tableViewCell file named "CapacitiesTableViewCell" that has 4 of the 8 labels as outlets.
SIDE NOTE: For the name of the cells I've just put a generic "Section #, Row #" for now but will change the line of code to cell.textlabel?.text = capacityID when I've figured the issue out...assuming that's the correct way to go about this.
Here is my code thus far:
class CapacitiesTableViewController: UITableViewController {
let capacityParameters = ["O.D", "Weight", "I.D", "Grade"]
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return capacityParameters.count
} else {
return 1
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("capacitiesCell", forIndexPath: indexPath)
//let capacityID = capacityParameters[indexPath.row]
cell.textLabel?.text = "Section \(indexPath.section) Row \(indexPath.row)"
return cell
let dimensionAndStrengthCell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
// my code goes here
return dimensionAndStrengthCell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Casing Specification"
} else {
return "Dimension & Strength"
}
}
This is my storyboard:
However when I run the app I get this:
CHANGING CELL HEIGHT FOR CELL2
So in my viewDidLoad I have the following:
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 200.0
}
I'm trying to have my view look like the screenshot below but the "Strength and Dimension" section cell height is stuck on the default height.
the new code I've added from Farhad's response is :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// init return type of cell
var returnCell: UITableViewCell!
if indexPath.section == 0 { // you can also check for section with section.index
returnCell = tableView.dequeueReusableCellWithIdentifier("capacitiesCell", forIndexPath: indexPath)
//let capacityID = capacityParameters[indexPath.row]
returnCell.textLabel?.text = "Section \(indexPath.section) Row \(indexPath.row)"
return returnCell
} else {
returnCell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 200.0
return returnCell
}
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Casing Specification"
} else {
return "Dimension & Strength"
}
}
In your cellForRowAtIndexPath you check at what position you need to enter a new row or section with if/else statement.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Init the Return of Cell Type
var returnCell: UITableViewCell!
if index.row == 0 { // You can also check for section index.section
returnCell = tableView.dequeueReusableCellWithIdentifier("capacitiesCell", forIndexPath: indexPath)
//let capacityID = capacityParameters[indexPath.row]
cell.textLabel?.text = "Section \(indexPath.section) Row \(indexPath.row)"
return returnCell
} else {
returnCell = //Other cell at other rows or section
return returnCell
}
}

Dynamic Dimension of UITableViewCell ok, but how hide them if empty?

I have a UITableView with 3 prototyped cells (ex. 1st cell: image, 2nd cell: Description, 3. Links,...).
I would like to hide them if for a cell the data from the backend is empty (Ex. if there is no image, hide the first cell). In order to do that, I have override the heightForRowAtIndexPath function in this way:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
switch (indexPath.row) {
case 0:
if event?.photo_urls.count == 0{
return 0
}
else{
return 80.0
}
case 1:
if event?.description == ""{
return 0
}
else{
return 90.0
}
default:
return 100.0
}
}
and hidden the cell by doing
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch (indexPath.row) {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("PhotoCell", forIndexPath: indexPath) as! UITableViewCell
if event?.photo_urls.count != 0 {
// ...
}
else{
cell.hidden = true
}
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("DesCell", forIndexPath: indexPath) as! UITableViewCell
if event?.description != "" {
// ...
}
else{
cell.hidden = true
}
return cell
default:
let cell = tableView.dequeueReusableCellWithIdentifier("PhotoCell", forIndexPath: indexPath) as! UITableViewCell
return cell
}
}
Until here no problem, it works properly!
Now, THE PROBLEM is that I would like to make the cells dynamics according to the cell contents (ex. description height). In order to do that, I have used
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80.0
}
and if I comment the heightForRowAtIndexPath the cells are actually dynamics but I can't hide them anymore.
Do you have any suggestion on how to be able to hide the cells if they are empty and apply the automatic dimension according to their content?
lets say you have dynamic data and you want to show it in tableview so you need to create an array of your data to display.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet
var tableView: UITableView
var items: [String] = ["We", "Heart", "nothing" ,"" ,"imageurl", "", "xyz"]
override func viewDidLoad() {
super.viewDidLoad()
reloadTableAfterSorting()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func reloadTableAfterSorting(){
for var i = 0; i < self.items.count; i++
{
if self.items[i] == ""{
self.items.removeAtIndex(2)
}
}
self.tableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
For that i recommend you to sort the array before displaying it in the table view. Hiding the cell is not a good idea and its not good according to Apple recommendations. So you can do one thing except hiding the cell: remove the index from the array. In this way you can always have data to show in table and it will behave properly. So don’t try to hide the cell just pop the index from array.

Adding UITextfield as cell's accessory view

I have 4 sections in my tableView, each section has single cell. When user selects the fourth section's cell, I want them to type on it. So I just added textfield as a accessoryView for that cell in didSelectRow function as shown below.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 3 {
//adding textField
theTextField = UITextField(frame: CGRectMake(10, 480, 300, 40))
theTextField.backgroundColor = UIColor.brownColor()
theTextField.placeholder = "Please type here...."
theTextField.textColor = UIColor.yellowColor()
theTextField.layer.cornerRadius = 10.0
selectedCell?.accessoryView = theTextField
}
But when I click on it, the keyboard is hiding that cell. I want the table view to scroll up. Please help me to resolve this or please please let me know if there is any other way to achieve this!
Not clear why you are implementing the cell specifications in "didSelectRowAtIndexPath" when a more logical place is in "cellForRowAtIndexPath". I implemented your code except putting the cell spec for section 3 in cellForRowAtIndexPath and the table cells are scrolled up as expected. See code below. To take advantage of the tableview scrolling the cell needs to be defined in cellRowForIndex. To meet your objective stated in the comment you can use the .hidden feature and insert the code as shown.
import UIKit
class: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 3 {
selectedCell.accessoryView?.backgroundColor = UIColor.brownColor()
selectedCell.accessoryView?.hidden = false
} else {
selectedCell.accessoryView?.hidden = true
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 4
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
if indexPath.section == 3 {
//adding textField
var theTextField = UITextField(frame: CGRectMake(10, 480, 300, 40))
theTextField.backgroundColor = UIColor.brownColor()
theTextField.placeholder = "Please type here...."
theTextField.textColor = UIColor.yellowColor()
theTextField.layer.cornerRadius = 10.0
cell.accessoryView = theTextField
cell.accessoryView?.hidden = true
}
else {
cell.textLabel!.text = "\(indexPath.section)"
}
return cell
}
}

Resources