Autosizing Cell After Subview Added Programatically - ios

In my code, I'm adding a subview to a subview inside each cell. Each nested subview can be of various sizes. The nested subview is not causing the cell to increase its height, so the subview is getting cut off. How can I get the cell to increase in height based on the nested subview?
import WSTagsField
class Search {
#IBOutlet weak var searchResultsTableView: UITableView!
override func viewDidLoad()
{
super.viewDidLoad()
searchResultsTableView.rowHeight = UITableView.automaticDimension
searchResultsTableView.estimatedRowHeight = 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var availableTagsString = ""
var matchingAvailableTagsWithSearch = [String]()
let availableTagsArray = documentKeysWithinRadius[indexPath.row]["available_tags"] as! [String]
for i in 0...availableTagsArray.count - 1
{
if searchTags.contains(availableTagsArray[i])
{
availableTagsString += "\(availableTagsArray[i]) "
matchingAvailableTagsWithSearch.append(availableTagsArray[i])
}
}
availableTagsString = availableTagsString.trimmingCharacters(in: .whitespacesAndNewlines)
let cell : MyCustomCell = self.searchResultsTableView.dequeueReusableCell(withIdentifier: "cell") as! MyCustomCell
let tagsField = MyFunctions().createTagsField(fontSize: 14.0)
//add the view only tags
if (matchingAvailableTagsWithSearch.count > 0)
{
for i in 0...matchingAvailableTagsWithSearch.count - 1
{
tagsField.addTag(matchingAvailableTagsWithSearch[i])
}
}
tagsField.readOnly = true
tagsField.frame = cell.tableCellTagsView.bounds
//tableCellTagsView is a UIView in the prototype cell
cell.tableCellTagsView.addSubview(tagsField)
return cell
}
}
Constraints

To resize a tableView you need to set constraints for any view added , I think the best way is to use a vertical stackView for that by hooking it's top , left , bottom and right constraints and use
cell.stackTags.addArranagedSubview(tagsField)
and give it a height constraint
tagsField.heightAnchor.constraint(equalToConstant:50).isActive = true
If the element you add is a UILabel/UIButton that has an intrinsic content size then no need for the height constraint
Also this
tagsField.frame = cell.tableCellTagsView.bounds
won't correctly get the real bounds as it's not yet known , plus frame-layout won't resize the cell
Plus to clear anything either by implement prepareForReuse inside the cell subclass , or remove all previously added subviews with
cell.stackTags.subviews.forEach { $0.removeFromsuperview() }
after this line
let cell:MyCustomCell = self.searchResultsTableView.dequeueReusableCell(withIdentifier: "cell") as! MyCustomCell
With no need for :MyCustomCell

Related

increase TableView height based on cells

I need to increase UITableView height based on UITableViewCell content size.
I'm using Custom Google Auto Complete. I have an UITextField. When I enter a letter in that UITextField it will call shouldChangeCharactersIn range delegate method.
Inside that method I will send dynamic request to Google AutoComplete API to get predictions result.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if tableView.numberOfRows(inSection: 0) > 0
{
let newLength = (textField.text?.characters.count)! + string.characters.count - range.length
let enteredString = (textField.text! as NSString).replacingCharacters(in: range, with:string)
if newLength == 0 {
tableView.isHidden = true
}
if newLength > 0
{
address.removeAll()
self.tableView.isHidden = false
GetMethod("https://maps.googleapis.com/maps/api/place/autocomplete/json?input=\(enteredString)&key=MYAPIKEY", completion: { (resultDict) in
let resultArray = resultDict.object(forKey: "predictions")! as! NSArray
print(resultArray.count)
for temp in resultArray
{
let newtemp = temp as! NSDictionary
let tempAdd = newtemp.value(forKey:"description") as! String
let placeId = newtemp.value(forKey:"place_id") as! String
var dict = [String : AnyObject]()
dict["address"] = tempAdd as AnyObject?
dict["latlong"] = placeId as AnyObject?
self.address.append(dict as AnyObject)
print(newtemp.value(forKey:"description"))
print(self.address.count)
self.tableView.reloadData()
}
})
}
return true
}
After I will store all address to Address array dynamically, I need to increase UITableView height based on that incoming address content.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "TableViewCell"
var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
if cell == nil
{
cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier)
}
let addresstoDisp = address[indexPath.row] as! NSDictionary
let name = addresstoDisp.value(forKey: "address")
cell?.textLabel?.numberOfLines = 0
cell?.translatesAutoresizingMaskIntoConstraints = false
cell?.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cell?.textLabel?.textAlignment = NSTextAlignment.center
cell?.textLabel?.text = name as! String
return cell!
}
UITableViewCell height is increasing perfectly. Also I need to increase tableView height.
Add these lines after your cells are created. Because it returns 0 height in viewDidLoad()
var frame = tableView.frame
frame.size.height = tableView.contentSize.height
tableView.frame = frame
UPDATE
//After Tableviews data source values are assigned
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.heightAnchor.constraint(equalToConstant: tableView.contentSize.height).isActive = true
The below code worked for me with UITableViewCell who has AutomaticDimension.
Create an outlet for tableViewHeight.
#IBOutlet weak var tableViewHeight: NSLayoutConstraint!
var tableViewHeight: CGFloat = 0 // Dynamically calcualted height of TableView.
For the dynamic height of the cell, I used the below code:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.estimatedRowHeight
}
For height of the TableView, with dynamic heights of the TableView Cells:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
print(cell.frame.size.height, self.tableViewHeight)
self.tableViewHeight += cell.frame.size.height
tableViewBillsHeight.constant = self.tableViewHeight
}
Explanation:
After the TableView cell is created, we fetch the frame height of the cell that is about to Display and add the height of the cell to the main TableView.
In your viewDidLoad write:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myTable.reloadData()
myTable.layoutIfNeeded()
}
Now override the viewDidLayoutSubviews method to give the tableview explicit height constraint:
override func viewDidLayoutSubviews() {
myTable.heightAnchor.constraint(equalToConstant:
myTable.contentSize.height).isActive = true
}
This makes sure that the tableview is loaded and any constraints related layout adjustments are done.
Without setting and resetting a height constraint you can resize a table view based on its content like so:
class DynamicHeightTableView: UITableView {
override open var intrinsicContentSize: CGSize {
return contentSize
}
}
I have been struggling with scroll view that must increase height when table view cells are loaded and also table view shouldn't be scrollable as it should display all cells (scroll is handled by scroll view). Anyway, you should use KeyValueObserver. First you create outlet for height constraint:
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
Then you add observation for table view:
private var tableViewKVO: NSKeyValueObservation?
After that, just add table view to observation and change height constraint size as your content size changes.
self.tableViewKVO = tableView.observe(\.contentSize, changeHandler: { [weak self] (tableView, _) in
self?.tableViewHeightConstraint.constant = tableView.contentSize.height
})
this is what works for me, very simple straight forward solution:
Create a new UIElement of the TableView height constraint and connecting it to the view
#IBOutlet weak var tableViewHeightConst: NSLayoutConstraint!
Then add the following wherever you are creating your cells, in my case I was using RxSwift
if self.tableView.numberOfRows(inSection: 0) > 0 {
//gets total number of rows
let rows = self.tableView.numberOfRows(inSection: 0)
//Get cell desired height
var cellHeight = 60
self.tableViewHeightConst.constant = CGFloat(rows * cellHeight)
}
this should do the trick.

Dynamic Table View Cell & Content Size conflicting & Pintrest Layout

In my requirement currently,I wanted to develop a Pintrest layout view. For that I have 2 tableView's on same ScrollView.
mark: I didn't use collectionView as it was very difficult to customize the flow Layout for this purpose & I do not want to include a 3rd Party framework for the same.
Check the attached screenshot -
I am populating them with 2 arrays one for even & one for odd items.
I am making these tableView's non scrollable & increasing my scrollView's contentView's height as per the tallest tableView. Both the tableView's have custom cells with dynamically increasing contents i.e a label.
in my viewDidLoad()
self.tableViewCol1.estimatedRowHeight = 296
self.tableViewCol1.rowHeight = UITableViewAutomaticDimension
self.tableViewCol1.separatorStyle = UITableViewCellSeparatorStyle.None
self.tableViewCol2.estimatedRowHeight = 296
self.tableViewCol2.rowHeight = UITableViewAutomaticDimension
self.tableViewCol2.separatorStyle = UITableViewCellSeparatorStyle.None
in my DataSource method -
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell2 = MostLikedTVCscnd()//custom Cell for 2nd Table
var cell1 = MostLikedTVC()//custom Cell for 1st Table
if tableView == tableViewCol1
{
let cell = tableViewCol1.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! MostLikedTVC
cell.imageCol1.image = imageArr1[indexPath.row] as? UIImage
cell.aboutLblCol1.text = labelArr1[indexPath.row] as? String//dynamic increasing label
cell1=cell
}
else if tableView == tableViewCol2
{
let cell = tableViewCol2.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath) as! MostLikedTVCscnd
cell.imageCol2.image = imageArr2[indexPath.row] as? UIImage
cell.aboutLblCol2.text = labelArr2[indexPath.row] as? String
cell2 = cell
}
//changing the height constraint of table's to make the table view non scrollable
tableView1HghtCnstrnt.constant = tableViewCol1.contentSize.height
tableView2HghtCnstrnt.constant = tableViewCol2.contentSize.height
//comparing the content size of table's to check which table is the tallest & adjust the height of the main ScrollView's content
if tableViewCol1.contentSize.height>tableViewCol2.contentSize.height
{
mainViewHghtCnstrnt.constant = tableViewCol1.contentSize.height+35//mainViewHghtCnstrnt :- mainScrollView's content height constraint & 35 is the padding
}
else if tableViewCol1.contentSize.height<tableViewCol2.contentSize.height
{
mainViewHghtCnstrnt.constant = tableViewCol2.contentSize.height+35
}
else if tableViewCol1.contentSize.height==tableViewCol2.contentSize.height
{
mainViewHghtCnstrnt.constant = tableViewCol2.contentSize.height+35
}
//returning the cell
if tableView == tableViewCol1
{
return cell1
}
else
{
return cell2
}
}
}
But my problem is that the table's are not properly calculating the size of their content's. I did some search and here an answer to a question says - contentSize will be messed up when you give estimatedRowHeight
So what options do I have? What can be done to realise the same properly?
You can use tableView's contentSize property to get the height required for the tableView.
I have done a demo to test this, and it is working as you are expecting.
My constraint are as follows:
ScrollView:
LeadingSpace to SuperView, TrailingSpace to SuperView, TopSpace to
SuperView, BottomSpace to SuperView
ContainerView (UIView inside scrollView):
LeadingSpace to SuperView, TrailingSpace to SuperView, TopSpace to
SuperView, BottomSpace to SuperView, EqualWidth to SuperView (i.e
scrollView)
ATableView
LeadingSpace to SuperView, TrailingSpace to BTableView, TopSpace to
SuperView, Width fixed points, HeightFixed points (Created outlet of
this constraint), BottomSpace to SuperView with StandardSpacing and
LowPriority (250)
BTableView
LeadingSpace to ATableView, TrailingSpace to SuperView, TopSpace to
SuperView, Width fixed points, HeightFixed points (Created outlet of
this constraint), BottomSpace to SuperView with StandardSpacing and
LowPriority (250)
And here is my full code,
class ViewController: UIViewController {
#IBOutlet weak var tableViewA: UITableView!
#IBOutlet weak var tableViewB: UITableView!
#IBOutlet weak var heightConstraintBTableView: NSLayoutConstraint!
#IBOutlet weak var heightConstraintATableView: NSLayoutConstraint!
var tableViewACellCount = 50
var tableViewBCellCound = 20
override func viewDidLoad() {
super.viewDidLoad()
//Dont set estimatedRowHeight as it is displaying empty cell at the bottom of tableView (try playing with this uncomment estimatedRowHeight)
//tableViewA.estimatedRowHeight = 44.0
tableViewA.rowHeight = UITableViewAutomaticDimension
//tableViewB.estimatedRowHeight = 44.0
tableViewB.rowHeight = UITableViewAutomaticDimension
//Try both methods
self.performSelector("adjustTableViewHeight", withObject: [], afterDelay: 0.0)
//self.adjustTableViewHeight()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if(tableView.isEqual(tableViewA)) { //A TableView
return tableViewACellCount
} else { //B TableView
return tableViewBCellCound
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell")
return cell!
}
func adjustTableViewHeight() {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.tableViewA.layoutIfNeeded()
self.tableViewB.layoutIfNeeded()
}
heightConstraintATableView.constant = tableViewA.contentSize.height
heightConstraintBTableView.constant = tableViewB.contentSize.height
}
}

How do I create a parallax effect in UITableView with UIImageView in their prototype cells

I'm building an app in iOS 8.4 with Swift.
I have a UITableView with a custom UITableViewCell that includes a UILabel and UIImageView. This is all fairly straight forward and everything renders fine.
I'm trying to create a parallax effect similar to the one demonstrated in this demo.
I currently have this code in my tableView.cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCellWithIdentifier("myitem", forIndexPath: indexPath) as! MixTableViewCell
cell.img.backgroundColor = UIColor.blackColor()
cell.title.text = self.items[indexPath.row]["title"]
cell.img.image = UIImage(named: "Example.png")
// ideally it would be cool to have an extension allowing the following
// cell.img.addParallax(50) // or some other configurable offset
return cell
}
That block exists inside a class that looks like class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource { ... }
I am also aware that I can listen to scroll events in my class via func scrollViewDidScroll.
Other than that, help is appreciated!
I figured it out! The idea was to do this without implementing any extra libraries especially given the simplicity of the implementation.
First... in the custom table view Cell class, you have to create an wrapper view. You can select your UIImageView in the Prototype cell, then choose Editor > Embed in > View. Drag the two into your Cell as outlets, then set clipToBounds = true for the containing view. (also remember to set the constraints to the same as your image.
class MyCustomCell: UITableViewCell {
#IBOutlet weak var img: UIImageView!
#IBOutlet weak var imgWrapper: UIView!
override func awakeFromNib() {
self.imgWrapper.clipsToBounds = true
}
}
Then in your UITableViewController subclass (or delegate), implement the scrollViewDidScroll — from here you'll continually update the UIImageView's .frame property. See below:
override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = self.tableView.contentOffset.y
for cell in self.tableView.visibleCells as! [MyCustomCell] {
let x = cell.img.frame.origin.x
let w = cell.img.bounds.width
let h = cell.img.bounds.height
let y = ((offsetY - cell.frame.origin.y) / h) * 25
cell.img.frame = CGRectMake(x, y, w, h)
}
}
See this in action.
I wasn't too happy with #ded's solution requiring a wrapper view, so I came up with another one that uses autolayout and is simple enough.
In the storyboard, you just have to add your imageView and set 4 constraints on the ImageView:
Leading to ContentView (ie Superview) = 0
Trailing to ContentView (ie Superview) = 0
Top Space to ContentView (ie Superview) = 0
ImageView Height (set to 200 here but this is recalculated based on the cell height anyway)
The last two constraints (top and height) need referencing outlets to your custom UITableViewCell (in the above pic, double click on the constraint in the rightmost column, and then Show the connection inspector - the icon is an arrow in a circle)
Your UITableViewCell should look something like this:
class ParallaxTableViewCell: UITableViewCell {
#IBOutlet weak var parallaxImageView: UIImageView!
// MARK: ParallaxCell
#IBOutlet weak var parallaxHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var parallaxTopConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
clipsToBounds = true
parallaxImageView.contentMode = .ScaleAspectFill
parallaxImageView.clipsToBounds = false
}
}
So basically, we tell the image to take as much space as possible, but we clip it to the cell frame.
Now your TableViewController should look like this:
class ParallaxTableViewController: UITableViewController {
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return cellHeight
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as! ParallaxTableViewCell
cell.parallaxImageView.image = … // Set your image
cell.parallaxHeightConstraint.constant = parallaxImageHeight
cell.parallaxTopConstraint.constant = parallaxOffsetFor(tableView.contentOffset.y, cell: cell)
return cell
}
// Change the ratio or enter a fixed value, whatever you need
var cellHeight: CGFloat {
return tableView.frame.width * 9 / 16
}
// Just an alias to make the code easier to read
var imageVisibleHeight: CGFloat {
return cellHeight
}
// Change this value to whatever you like (it sets how "fast" the image moves when you scroll)
let parallaxOffsetSpeed: CGFloat = 25
// This just makes sure that whatever the design is, there's enough image to be displayed, I let it up to you to figure out the details, but it's not a magic formula don't worry :)
var parallaxImageHeight: CGFloat {
let maxOffset = (sqrt(pow(cellHeight, 2) + 4 * parallaxOffsetSpeed * tableView.frame.height) - cellHeight) / 2
return imageVisibleHeight + maxOffset
}
// Used when the table dequeues a cell, or when it scrolls
func parallaxOffsetFor(newOffsetY: CGFloat, cell: UITableViewCell) -> CGFloat {
return ((newOffsetY - cell.frame.origin.y) / parallaxImageHeight) * parallaxOffsetSpeed
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = tableView.contentOffset.y
for cell in tableView.visibleCells as! [MyCustomTableViewCell] {
cell.parallaxTopConstraint.constant = parallaxOffsetFor(offsetY, cell: cell)
}
}
}
Notes:
it is important to use tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) and not tableView.dequeueReusableCellWithIdentifier("CellIdentifier"), otherwise the image won't be offset until you start scrolling
So there you have it, parallax UITableViewCells that should work with any layout, and can also be adapted to CollectionViews.
This method works with table view and collection view.
first of all create the cell for the tableview and put the image view in it.
set the image height slightly more than the cell height. if cell height = 160 let the image height be 200 (to make the parallax effect and you can change it accordingly)
put this two variable in your viewController or any class where your tableView delegate is extended
let imageHeight:CGFloat = 150.0
let OffsetSpeed: CGFloat = 25.0
add the following code in the same class
func scrollViewDidScroll(scrollView: UIScrollView) {
// print("inside scroll")
if let visibleCells = seriesTabelView.visibleCells as? [SeriesTableViewCell] {
for parallaxCell in visibleCells {
var yOffset = ((seriesTabelView.contentOffset.y - parallaxCell.frame.origin.y) / imageHeight) * OffsetSpeedTwo
parallaxCell.offset(CGPointMake(0.0, yOffset))
}
}
}
where seriesTabelView is my UItableview
and now lets goto the cell of this tableView and add the following code
func offset(offset: CGPoint) {
posterImage.frame = CGRectOffset(self.posterImage.bounds, offset.x, offset.y)
}
were posterImage is my UIImageView
If you want to implement this to collectionView just change the tableView vairable to your collectionView variable
and thats it. i am not sure if this is the best way. but it works for me. hope it works for you too. and let me know if there is any problem
After combining answers from #ded and #Nycen I came to this solution, which uses embedded view, but changes layout constraint (only one of them):
In Interface Builder embed the image view into a UIView. For that view make [√] Clips to bounds checked in View > Drawing
Add the following constraints from the image to view: left and right, center Vertically, height
Adjust the height constraint so that the image is slightly higher than the view
For the Align Center Y constraint make an outlet into your UITableViewCell
Add this function into your view controller (which is either UITableViewController or UITableViewControllerDelegate)
private static let screenMid = UIScreen.main.bounds.height / 2
private func adjustParallax(for cell: MyTableCell) {
cell.imageCenterYConstraint.constant = -(cell.frame.origin.y - MyViewController.screenMid - self.tableView.contentOffset.y) / 10
}
Note: by editing the magic number 10 you can change how hard the effect will be applied, and by removing the - symbol from equation you can change the effect's direction
Call the function from when the cell is reused:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCellId", for: indexPath) as! MyTableCell
adjustParallax(for: cell)
return cell
}
And also when scroll happens:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
(self.tableView.visibleCells as! [MyTableCell]).forEach { cell in
adjustParallax(for: cell)
}
}

TableViewCell overlaps text

I'm having a problem with my TableViewCell
I have two type of cell in my storyboard.
when i scroll, the text overlaps in some cells. I Try everything but I do not know how else to do. thank you very much for the help
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var storeNew = systemsBlog.getStore(listNews[indexPath.row].getIdStore())
var newNotice = listNews[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("TimelineCell", forIndexPath: indexPath) as? TimelineCell
cell!.nameLabel.text = storeNew.getName()
cell!.postLabel?.text = newNotice.getText()
cell!.postLabel?.numberOfLines = 0
cell!.dateLabel.text = newNotice.getDate()
cell!.typeImageView?.tag = indexPath.row;
return cell!
}
class TimelineCell : UITableViewCell {
#IBOutlet var nameLabel : UILabel!
#IBOutlet var postLabel : UILabel?
#IBOutlet var dateLabel : UILabel!
override func awakeFromNib() {
postLabel?.font = UIFont(name: "Roboto-Thin", size: 14)
}
override func layoutSubviews() {
super.layoutSubviews()
}
I can fix the problem. In the storyboard, the label have unchacked "Clears Graphics Context". I checked and for now it solved! Thanks for the help!
I had a similar issue with one of my UITableViews in the past. There are a bunch of things that could cause this, but maybe it is the same thing that happened to me.
I see that you are using a custom tableViewCell. What could be happening is when you set the text of the cell, it adds a label view with that text. Now say you scroll through the tableview and that cell disappears. If you were to reuse that cell and you did not remove the label from the subview, or set the text of that label again to the desired text, you will be reusing the tableviewcell with a previous label on it and adding a new label with new text to it, overlapping the text.
My suggestion would be to make sure you do not keep adding UIlabels as subviews in the TimelineCell class unless no label exists. if a label exists edit the text of that label not of the cell.
As per apple documentation:
The table view’s data source implementation of
tableView:cellForRowAtIndexPath: should always reset all content when
reusing a cell.
It seems that your problem is that you not always setting postLabel, causing it to write on top of the other cells, try this:
//reuse postLabel and set to blank it no value is returned by the function
let cell = tableView.dequeueReusableCellWithIdentifier("TimelineCell", forIndexPath: indexPath) as? TimelineCell
cell!.nameLabel.text = storeNew.getName()
if newNotice.getText() != nil{
cell!.postLabel.text = newNotice.getText()
} else {cell!.postLabel.text = ''}
cell!.postLabel.numberOfLines = 0
cell!.dateLabel.text = newNotice.getDate()
cell!.typeImageView?.tag = indexPath.row;
return cell!
}
//Make postLabel mandatory and set the font details in the xcode
class TimelineCell : UITableViewCell {
#IBOutlet var nameLabel : UILabel!
#IBOutlet var postLabel : UILabel!
#IBOutlet var dateLabel : UILabel!
override func awakeFromNib() {
//set this in xcode
}
override func layoutSubviews() {
super.layoutSubviews()
}
Also be sure that you are not creating any UI element and appending to the cell, as if you are you need to dispose it before you recycle the cell.
You can try setting:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 44.0 // Set this value as a good estimation according to your cells
}
In the View Controller containing your tableView.
Make sure the layout constraints in your TimelineCell define a clear line of height constraints
Another option is responding to:
tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 44.0 // Your height depending on the indexPath
}
Always in the ViewController that contains your tableView and, I assume, is the tableView's UITableViewDelegate
Hope this helps
Set cell to nil that will fix some error.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? ImageCellTableViewCell
cell = nil
if cell == nil {
cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for:indexPath) as? ImageCellTableViewCell
}
cell.yourcoustomtextTextLabel.text = "this is text"
cell.yourcoustomtextImageView.image = image
return cell
}

Single line text takes two lines in UILabel

As you can see in the picture the middle cell has a UILabel that consumes two lines, but the text is actually a single line. It seems that if the text needs only few characters to create a new line, iOS assumes that it already has 2 lines. This is odd.
This is how I create the label:
self.titleLabel.lineBreakMode = .ByTruncatingTail
self.titleLabel.numberOfLines = 0
self.titleLabel.textAlignment = .Left
The constraints are set once:
self.titleLabel.autoPinEdgeToSuperviewEdge(.Top)
self.titleLabel.autoPinEdgeToSuperviewEdge(.Leading)
self.titleLabel.autoPinEdgeToSuperviewEdge(.Trailing)
self.titleLabel.autoPinEdgeToSuperviewEdge(.Bottom)
The weird thing is, when the table is scrolled so that the odd cell disappears and scrolled back again it has its normal height. After scroll:
Any ideas whats wrong? I am using swift, xcode6.1 and iOS8.1
TableViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(CityTableViewCell.self, forCellReuseIdentifier:"cell")
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 52
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell: CityTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as? CityTableViewCell {
// Configure the cell for this indexPath
cell.updateFonts()
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
if indexPath.row == 1 {
cell.titleLabel.text = "Welcome to city 17, Mr. Gordon F."
} else {
cell.titleLabel.text = "Lamar!!!"
}
// Make sure the constraints have been added to this cell, since it may have just been created from scratch
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
return cell
}
return UITableViewCell();
}
I think you met this bug: http://openradar.appspot.com/17799811. The label does not set the preferredMaxLayoutWidth correctly.
The workaround I chose was to subclass UITableViewCell with the following class:
class VFTableViewCell : UITableViewCell {
#IBOutlet weak var testoLbl: UILabel!
//MARK: codice temporaneo per bug http://openradar.appspot.com/17799811
func maxWidth() -> CGFloat {
var appMax = CGRectGetWidth(UIApplication.sharedApplication().keyWindow.frame)
appMax -= 12 + 12 // borders, this is up to you (and should not be hardcoded here)
return appMax
}
override func awakeFromNib() {
super.awakeFromNib()
// MARK: Required for self-sizing cells.
self.testoLbl.preferredMaxLayoutWidth = maxWidth()
}
override func layoutSubviews() {
super.layoutSubviews()
// MARK: Required for self-sizing cells
self.testoLbl.preferredMaxLayoutWidth = maxWidth()
}
}
OP-Note:
It seems that auto layout does not calculate the width of the UILabel layout correctly. Setting the preferred width to the parents width in my UITableViewCell subclass solves my problem:
self.titleLabel.preferredMaxLayoutWidth = self.frame.width
Found on SO: https://stackoverflow.com/a/19777242/401025

Resources