I have a label in a custom cell and it won't resize. Here is the code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell2 = tableView.dequeueReusableCellWithIdentifier("privatecell1", forIndexPath: indexPath) as! YourPrivateControllerCell
myCell2.barfront1.frame.size.width = 200
myCell2.barfront1.layer.masksToBounds = true
return myCell2
}
I know I can set the width by adding a constraint but the label will be a different size for every row. The code works for a normal view controll view but doesn't seem to work for a tableview cell/row. The actual code will be:
myCell2.barfront1.frame.size.width = myCell2.barback1.frame.size.width * percent
but I cant even get the label to resize to 200.
You can use constraints and connect the constraints with #IBOutlet like this. Of course you have to put this in the YourPrivateControllerCell.
#IBOutlet weak var labelWidth: NSLayoutConstraint? = nil
Then call the following method will be change the width of you label.
labelWidth.constant = 200
Good Luck.
I got it to work. Instead of setting a width for the label I added a layout width constraint. Code:
myCell2.barfront1.addConstraint(NSLayoutConstraint(item: myCell2.barfront1, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: myCell2.barback1.frame.size.width * barwidth1[indexPath.row] * 0.01))
Now the label is a different size for every row.
Related
I designed a drop down menu for Swift 4. In order to do this I used a button and tableview. I am trying to center the imageview() by using cell.imageview but I get an error.
Here is my code :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
customImageView.translatesAutoresizingMaskIntoConstraints = false
cell.backgroundColor = UIColor.white
cell.imageView?.image = dropDownOptions[indexPath.row].bankImage
// Constraints that i used.
cell.imageView?.addConstraint(NSLayoutConstraint(item: cell.contentView, attribute: .centerX, relatedBy: .equal, toItem: customImageView, attribute: .centerX, multiplier: 1.0, constant: 0))
cell.imageView?.addConstraint(NSLayoutConstraint(item: cell.contentView, attribute: .centerY, relatedBy: .equal, toItem: customImageView, attribute: .centerY, multiplier: 1.0, constant: 0))
return cell
}
Here is error when I try to give constraints.
Full Project
DropDownExample[4701:223823] [LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x60c000285190 UITableViewCellContentView:0x7f9386d09ad0.centerX == UIImageView:0x7f9386d04cf0.centerX (inactive)>
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
2018-10-11 17:13:24.499450+0300 DropDownExample[4701:223823] [LayoutConstraints] View hierarchy unprepared for constraint.
First better don't create a new UITableViewCell() better use:
tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier")
This will create new cells only if needed. But be aware of the reuse case.
In your case you always get a UITableViewCell with default style. This results in an image and a textlabel. You can't customize this kind of cell well.
Better create a prototype cell inside your storyboard and dequeue it. A custom cell can get customized completely.
If you don't wont to use storyboards - add a Custom TableViewCell class with a xib file and register it for reuse within the tableview.
tableView.register(UINib(nibName: "myCell", bundle: nil), forCellReuseIdentifier: "myCell")
I have a UITableView which has multiple UIStackView as items in it. In those UIStackView I add colored squares as subviews. The problem is, it heavily slows down the vertical scrolling.
How do I improve scrolling performance?
Here is how scrolling looks:
https://gfycat.com/DentalContentIndigowingedparrot
Here is the source code:
https://github.com/Cook10/StackTableProblem
You are missing the point of reusing cells. Your code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MyTableViewCell
addStacksToTableCell(cell.stack1)
addStacksToTableCell(cell.stack2)
addStacksToTableCell(cell.stack3)
addStacksToTableCell(cell.stack4)
return cell
}
everytime you scroll down and a new cell is displayed, you are removing all views inside it (400 views) and your are creating new views (400 views), triggering the expensive layout and rerendering mechanism.
That happens every time your scroll.
This completely defeats the purpose of reusing cells. It's the same as creating a new cell every time.
Instead, create the views only once and when reusing the cells, only update the color of the internal views. Don't recreate views just to change their color.
Using some very naive changes:
Add to your cell:
var createdViews: Bool = false
var views1: [UIView] = []
var views2: [UIView] = []
var views3: [UIView] = []
var views4: [UIView] = []
Then replace your controller methods with:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
if !cell.createdViews {
cell.views1 = addStacksToTableCell(cell.stack1)
cell.views2 = addStacksToTableCell(cell.stack2)
cell.views3 = addStacksToTableCell(cell.stack3)
cell.views4 = addStacksToTableCell(cell.stack4)
cell.createdViews = true
} else {
// only update the views
cell.views1.forEach { $0.backgroundColor = UIColor.random() }
cell.views2.forEach { $0.backgroundColor = UIColor.random() }
cell.views3.forEach { $0.backgroundColor = UIColor.random() }
cell.views4.forEach { $0.backgroundColor = UIColor.random() }
}
return cell
}
func addStacksToTableCell(_ stack: UIStackView) -> [UIView] {
stack.removeAllArrangedSubviews()
var views: [UIView] = []
for i in 0..<ITEMS{
let image = UIImageView()
image.backgroundColor = UIColor.random()
stack.addArrangedSubview(image)
let width = NSLayoutConstraint(item: image, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50)
let height = NSLayoutConstraint(item: image, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50)
image.addConstraints([width,height])
views.append(image)
}
return views
}
It's not 100% connected to this particular case, but another way to improve cell scrolling is :
In iOS, you can use Auto Layout to define the height of a table view cell; however, the feature is not enabled by default.
Normally, a cell’s height is determined by the table view delegate’s tableView:heightForRowAtIndexPath: method. To enable self-sizing table view cells, you must set the table view’s rowHeight property to UITableViewAutomaticDimension. You must also assign a value to the estimatedRowHeight property. As soon as both of these properties are set, the system uses Auto Layout to calculate the row’s actual height.
tableView.estimatedRowHeight = 85.0
tableView.rowHeight = UITableViewAutomaticDimension
Next, lay out the table view cell’s content within the cell’s content view. To define the cell’s height, you need an unbroken chain of constraints and views (with defined heights) to fill the area between the content view’s top edge and its bottom edge. If your views have intrinsic content heights, the system uses those values. If not, you must add the appropriate height constraints, either to the views or to the content view itself.
Additionally, try to make the estimated row height as accurate as possible. The system calculates items such as the scroll bar heights based on these estimates. The more accurate the estimates, the more seamless the user experience becomes.
Wish you all the best.
I have attached a UItextView in tableview cell. Now I want to insert an UIimage behind it like a chat bubble.But everytime I scroll the tableview , the Uitextview becomes smaller in width and longer in height. The text also gets resized from single to multi line. Also ,the same thing happens when I scroll back to an earlier cell ( for example the first row).
I can't figure out why this is happening.
My code is:
(SampleTableViewCell is my custom UItableViewCell class and lblTitle is my UitextView outlet.)
func tableView(tableView: UITableView, willDisplayCell cell: SampleTableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
{
cell.lblTitle.removeConstraints(cell.lblTitle.constraints)
let stringTitle = carName[indexPath.row] as String //carName is an array
cell.lblTitle.text=stringTitle
cell.lblTitle.sizeToFit()
let h = cell.lblTitle.frame.size.height
let w = cell.lblTitle.frame.size.width
let constraints = NSLayoutConstraint(item: cell.lblTitle, attribute: .Width, relatedBy: .Equal, toItem: cell.lblTitle, attribute: .Height, multiplier: 0, constant: w)
cell.lblTitle.addConstraint(constraints)
let kl = UIImage(imageLiteral: "bubble")
let imageView = UIImageView(frame:CGRectMake(0, 0, w, h))
imageView.image = kl
imageView.alpha = 0.4
cell.lblTitle.addSubview(imageView)
cell.lblTitle.sendSubviewToBack(imageView)
}
It is the problem of autolayout.
You are just giving height and width constraint to textView. what about x and y position?
And you haven't set constraints for your imageview!!
If you are giving constraint to one object then you required to give every object related to it.
So set proper constraints and your problem will be solved.
Instead of adding bubble imageview and constraints programmatically,using storyboard you can add imageview in the tableview cell first and then UITextView of same size as imageview and add constraints.Then you can add your logic at cellForRowAtIndexPath delegate for lblTitle's text and bubble image. Make sure to set lblTitle's background to ClearColor.
I have a label in the cell, now i am customize my cell and added a label into the cell. I am creating a chatting apps, however on the top of the conversation , i need to determine the top constraint suppose to given to the profile image or the message label, if the message label only got one line , the top constraint is given to the profile image, if the message label is taller than the profile image, i will give the constraint to the message label. Ok the logic sound good, but the problem is come, i added the above logic under the initialiser as below
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
print(messageLabel.frame.height)
}
However, when the cell is being created, the print result gave to me is 0, in the end i suspect the message label is havent created so the height is 0.
In this case, i use the following function
override func layoutSubviews() {
super.layoutSubviews()
if messageLabel.frame.height != 0
{
if self.messageLabel.tag != 1
{
messageLabel.tag = 1
if (bubbleImageView.frame.height > 60)
{
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant: 4.5))
} else
{
contentView.addConstraint(NSLayoutConstraint(item: profileImageView, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant: 4.5))
}
}
}
}
However it is work, the message label returned me the value of the height, but there is a bug on the above code which is, the cell is display first, the code only can be execute , in this case , when i debug it in the simulator, it took one second before execute the code, in this case, the user will see how the cell transform from none constraint to have constraint which is a bad practice for the user experience. In the end, i have to return back how to get the number of lines of the message label before the cell being displayed. So i can i achieve it ?
You don't want to do this in func layoutSubviews() since your code now will add the constraint several times (func layoutSubviews() is executed more than once). The issue you are having is pretty much what your conclusion is, the label hasn't been laid out yet since this will happen when layoutSubviews() is executed. What you can do however is to force the layoutSubviews() to run before you add your constraint.
I don't really understand which views needs to be forced though, but in viewDidLoad for your UITableViewController you do
view.setNeedsLayout()
view.layoutIfNeeded()
and in your init for your cell you do the same for self. Maybe even for the label itself.
This function will return the number of line tableview will show
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myArray.count
}
It is numberOfRowsInSection from tableView delegate function
I have a UITableView with custom cells that also contain UITableViews. I'm having problems getting the height for these cells.
I'm getting the height in heightForRowAtIndexPath and to do this I populate the child tableView (within the cell) and call layoutIfNeeded. This gives me the correct contentSize for this tableView. The problem is that the overall size for the cell is wrong and doesn't change. So calling cell.bounds.height returns an incorrect value.
let cell:GroupCell = tableView.dequeueReusableCellWithIdentifier("GroupCell") as GroupCell
cell.configure(field as SingleField, delegate: self) // populates data
cell.tableView.layoutIfNeeded()
// cell.tableView.contentSize is correct
return cell.bounds.height // is wrong - bounds haven't changed
Can anyone point me in the right direction ? BTW - I'm targeting iOS8 only.
Try to create subclass of UITableViewCell, add TableView delegates to it, use it like usual.
I think the problem is in UITableView. You've sad table have correct content size, but it have incorrect size. After calculating the correct content size you should change tableView height manually. I think it will help you. Tip: you can observe contentSize property and every time it will be changed you can resize table view. To do this - add height constrain to your table with low priority (750 for example) and than just change it when needed. In this case your table will have both content size and self.frame.heigh equal.
I managed to resolve this as follows:
I put a height constraint (Greater than or equal to) on the UITableView (within the Cell). I set the priority of this to High
I set the priority of the bottom margin to Low
When configuring my custom cell I call setNeedsUpdateConstraints(), which invalidates the height constraint by removing it, and then recalculates it from the content size of the table view before adding it back.
Finally, in heightForRowAtIndexPath cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height now returns the correct height
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let field = fields[indexPath.row]
let cell:GroupCell = tableView.dequeueReusableCellWithIdentifier("GroupCell") as GroupCell
cell.configure(field as SingleField, delegate: self)
return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
}
func configure(field:SingleField,delegate:GroupDelegate){
self.field = field
self.delegate = delegate
nameLabel.text = field.name
tableView.reloadData()
setNeedsUpdateConstraints()
}
override func updateConstraints() {
tableView.removeConstraint(tableViewHeightConstraint)
tableViewHeightConstraint = NSLayoutConstraint(item: tableView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: tableView.contentSize.height)
tableView.addConstraint(tableViewHeightConstraint)
super.updateConstraints()
}