I had tried this method and I am using a label as a subview to the table cell the text in the label gets overlapped with the previous cell label content while scrolling
var tableCell: CommentTableViewCell? = tableView.dequeueReusableCellWithIdentifier("CommentCell") as? CommentTableViewCell
tableCell!.selectionStyle = UITableViewCellSelectionStyle.None;
if (tableCell == nil) {
println("table view is working")
tableCell = CommentTableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier:"CommentCell")
}
}
You have to set property for each cell in cellAtIndexPath. This kind of things mostly happen when you have images, but is reproducible with text as well. Lets say that you have custom cell, then you will have to check every time if that particular row has label, text, image etc.
Why does this kind of behavior happen? Because each of your cells are reused, and if reused cell does contain text that you don't have in a "new" cell, or image, or whatever, that will remain and create you an inconsistent bug.
How do you resolve it?
Try to set empty text when you get the cell. If your cell is custom, set both your label to empty string and default cells title label to empty string. Then retrieve your comment from array and assign it. If you provide some more code I will post you what should be changed.(I need cellAtIndexPath method).
So this is your code:
var tableCell: CommentTableViewCell? = tableView.dequeueReusableCellWithIdentifier("CommentCell") as? CommentTableViewCell
let label:UILabel = UILabel(frame: CGRectMake(60, 25, 255, 50))
label.attributedText = text1 label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping;
let font = UIFont(name: "Helvetica", size: 13.0)
label.font = font label.sizeToFit();
tableCell!.addSubview(label)
As I said in comment, your problem is adding label for each reused cell which will create overlapping. To fix this try:
var tableCell: CommentTableViewCell? = tableView.dequeueReusableCellWithIdentifier("CommentCell") as? CommentTableViewCell
if(!tableCell)
{
let label:UILabel = UILabel(frame: CGRectMake(60, 25, 255, 50))
label.attributedText = text1 label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping;
let font = UIFont(name: "Helvetica", size: 13.0)
label.font = font label.sizeToFit();
label.tag = 1; //This is tag added
// add text
tableCell!.addSubview(label)
}
else
{
let label : UILabel = (UILabel *)cell.viewWithTag(1) as UILabel?
// add text
}
Related
I am writing an application in Swift 3 that involves a UITableView. I am creating my table view cells pragmatically. The problem I am experiencing is that the cell's label text is persistent after the .reloadData() function. Is there a function to clear all elements and formatting from a cell before it's use? I am instantiating the UILabel via code and cannot access it across iterations. Unfortunately, I have already put a ton of time into programmatically creating the table, so I'd like to avoid going back and making a cell class if possible.
I am planning to use a function something like this:
var label: UILabel
if reloadCount > 0 {
//clear all formatting from the cell
}
label = UILabel(frame: CGRect(x: 30, y: 0, width: self.screenWidth - 100, height: 50))
label.textAlignment = .center
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.textColor = UIColor(red: 127.0/255, green: 140.0/255, blue: 142.0/255, alpha: 1.0)
label.font = UIFont(name: "Tahoma", size: CGFloat(22))
label.numberOfLines = 0
label.tag = Int(self.thoughtFeed[indexPath.row].dbId!)!
label.text = thoughtFeed[indexPath.row].thoughtText!.fromBase64()
label.isUserInteractionEnabled = true
label.addGestureRecognizer(labelTap)
cell.addSubview(label)
Thank you very much for the help.
You'll need to somehow "remember" the UILabel and either reuse it or remove it. Since you say you don't want to subclass UITableViewCell you can use tags to get a reference to your label. In your cellForRowAtIndexPath do something like this:
if let label = cell.viewWithTag(42) as? UILabel {
// now you have a reference to the existing label.
// Just update the text (or whatever you need to do)
} else {
label = UILabel(frame: CGRect(x: 30, y: 0, width: self.screenWidth - 100, height: 50))
// add a tag so we can remember it when we dequeue a cell later
label.tag = 42
cell.addSubview(label)
}
I have a UILabel which has been created programmatically and want to set the text property dynamically after its creation. I can word wrap it during init but not subsequently. After the title label text is changed it keeps one line, regardless of whether I set the number of lines to 2 or 0 and word wrap or not. After I change the title I want it to occupy 2 lines and the text is easily long enough for this to happen but it does not.
let titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .clear
label.layer.masksToBounds = true
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.textColor = .white
return label
}()
var titleLabelText: String? {
didSet {
titleLabel.text = titleLabelText
titleLabel.layoutIfNeeded()
}
}
then later...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
titleLabelText = "some string which needs word wrapping"
}
titleLabel.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: screenWidth - 90).isActive = true
Font size is UIFont(name: "Muli", size: 12)
Here is how I load my label in viewDidLoad() but the label.text is applied in viewDidAppear()
let headerStackView: UIStackView = {
let headerArray = [welcomeLabel]
let stackView = UIStackView(arrangedSubviews: headerArray)
When you set your label to display multiple line..Remember your label must have enough height that can hold your text to be display multiple line.
Try to increase height of your label, Check width is proper and then check with your code.
If your text gets increases decrease the font size, it will automatically show complete text in your label.
and set the label line space to 0 and allow word wrap property true
select your label and try this one
AFAIK You need to add it as attributedText, then create an NSAttributedString, with your string and attributes.
let paragraphStyleWithWordWrapping = NSMutableParagraphStyle()
paragraphStyleWithWordWrapping.lineBreakMode = .byWordWrapping
let attributedString = NSAttributedString(myString, [NSParagraphStyleAttributeName: paragraphStyleWithWordWrapping])
label.attributedText = attributedString
I am using Swift 3, Xcode 8.2.
I've been able to create a label to cover the empty table view cells when there are none to display.
My code is below and it is located in the subclass of UITableViewController.
override func numberOfSections(in tableView: UITableView) -> Int {
// if there are scans to display...
if items.count > 0 {
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
return 1
}
else { // otherwise, return 0, remove cell lines, and display a Label
let rect = CGRect(x: 0,
y: 0,
width: tableView.bounds.size.width,
height: tableView.bounds.size.height)
let noScanLabel: UILabel = UILabel(frame: rect)
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
tableView.backgroundView = noScanLabel
tableView.separatorStyle = .none
return 0
}
}
Here is the result.
Looks fine. But how, do I make it such that I include another line of text with a downward arrow pointing at the raised center button. Something like "Click here to start a scan"?
I've tried adding new line characters to the noScanLabel.text field but that didn't work out. Any pointers in the right direction would be helpful.
The simple solution is to set numberOfLines to 0 on noScanLabel. This way, the new lines will show.
let noScanLabel: UILabel = UILabel(frame: rect)
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.numberOfLines = 0
noScanLabel.textAlignment = NSTextAlignment.center
Note than in such cases, I would recommend, for better maintainability, to actually remove the TableView from the UIViewController (hence not inherit from UITableViewController) and replace it with an empty view when you detect no scans are available. This will make each state more independent of each other and make maintenance easier.
There are a few ways achieve your goal. There is a well known library called DZNEmptyDataSet for handling empty tableviews and collectionviews . https://github.com/dzenbot/DZNEmptyDataSet
The other way would be to create a uiview with your specified rect and then add two labels to that uiview. One would be your noScanLabel and the other would be a label or image containing your arrow. You can set the layout constraints as required so that the arrow appears pointing down.
This code seems to work well. Change constraints if needed
let rect = CGRect(x: 0, y: 0, width: tableview.bounds.size.width, height: tableview.bounds.size.height)
let noDataView = UIView(frame: rect)
let noScanLabel = UILabel()
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
let arrowLabel = UILabel()
arrowLabel.text = "Add Arrow Image to this label"
arrowLabel.textColor = UIColor.gray
arrowLabel.font = UIFont.boldSystemFont(ofSize: 24)
arrowLabel.textAlignment = NSTextAlignment.center
noScanLabel.widthAnchor.constraint(equalToConstant: 100)
arrowLabel.widthAnchor.constraint(equalToConstant: 50)
noDataView.addSubview(noScanLabel)
noDataView.addSubview(arrowLabel)
arrowLabel.translatesAutoresizingMaskIntoConstraints = false
noDataView.translatesAutoresizingMaskIntoConstraints = false
noScanLabel.translatesAutoresizingMaskIntoConstraints = false
self.tableview.addSubview(noDataView)
noDataView.isHidden = false
noDataView.centerXAnchor.constraint(equalTo: self.tableview.centerXAnchor).isActive = true
noDataView.centerYAnchor.constraint(equalTo: self.tableview.centerYAnchor).isActive = true
noScanLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
noScanLabel.topAnchor.constraint(equalTo: noDataView.centerYAnchor).isActive = true
arrowLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
arrowLabel.topAnchor.constraint(equalTo: noScanLabel.bottomAnchor).isActive = true
The other option is to set number of lines to zero as mentioned already
noScanLabel.numberLines = 0
You can take UIView and add your all UILabel and arrow Image on UIView and then assign that UIView to backgroundView of TableView.
Like this.
override func numberOfSections(in tableView: UITableView) -> Int {
// if there are scans to display...
if items.count > 0 {
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
return 1
}
else { // otherwise, return 0, remove cell lines, and display a Label
let rect = CGRect(x: 0,
y: 0,
width: tableView.bounds.size.width,
height: tableView.bounds.size.height)
let messageBaseView = UIView(frame: rect)
//Add your first label..
let noScanLabel: UILabel = UILabel()
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
messageBaseView.addSubView(noScanLabel)
//Add your second label.. and your arrow image here on messageBaseView
//Assign messageBaseView to backgroundView of tableView
tableView.backgroundView = messageBaseView
tableView.separatorStyle = .none
return 0
}
}
I have a custom UICollectionViewCell which I am overwriting. It has a property called nameLabel of type UILabel. I set initialize it without a frame and set the numberOfLines to 3.
let nameLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 3
label.sizeToFit()
return label
}()
The name label has the following VFL constraint applied to it:
addConstraintsWithFormat("V:[v0]-10-[v1]-10-|", views: nameLabel, userLabel)
where addConstraintsWithFormat is defined as the following from Brian Voong's UICollectionView example:
extension UIView {
func addConstraintsWithFormat(format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerate() {
let key = "v\(index)"
viewsDictionary[key] = view
view.translatesAutoresizingMaskIntoConstraints = false
}
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
}
What happens is, say I have one cell whose nameLabel text is rendered in three lines (enough lines to merit three lines of wrapping, given the constraints).
So what happens is, I scroll to that cell, it shows the nameLabel as having three lines, I scroll past that cell. When I scroll back up to that cell, the very same UILabel now has one line.
How can I keep the flexible display of the UILabel size based on the content it renders?
I was able to reproduce your error, implementing it the way you have above makes the error...
I didn't see how you implemented it inside your -cellForItemAtIndexPath though, Fixed it with some modification from what you have provided, here is my -cellForItemAtIndexPath looks like:
//--
// UICollectionView Delegates
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cvCell", forIndexPath: indexPath)
// just to remove multi existence of identical views
//
cell.subviews.forEach { (view) in
view.removeFromSuperview()
}
let nameLabel: UILabel = {
let label = UILabel()
// declare constant configurations here
//
label.numberOfLines = 3
label.textAlignment = NSTextAlignment.Center
label.text = "Index: \(indexPath.row)\n and this is quite a long text that will occuply 3 lines"
return label
}()
// this is just an example base frame
//
nameLabel.frame = CGRectMake(0, 0, cell.frame.size.width/*your width*/, CGFloat.max)
nameLabel.sizeToFit()
cell.addSubview(nameLabel)
// ...
//
// updates constraints here
return cell
}
Hope this would help you.. Cheers! :)
I ended up solving the error but setting Visual Format Strings of Auto Layout Constraints.
I hadn't applied the horizontal constraint, neither had I given the UILabel a fixed width. My new constraints are the following:
addConstraintsWithFormat("H:|-20-[v0(200)]", views:nameLabel)
addConstraintsWithFormat("V:[v0]-10-[v1]-10-|", views: nameLabel, userLabel)
where as before I only had
addConstraintsWithFormat("V:[v0]-10-[v1]-10-|", views: nameLabel, userLabel)
I didn't use sizeToFit() at all. Only the following were my UILabel settings in my UICollectionViewCell class.
let nameLabel: UILabel = {
let label:UILabel = UILabel(frame: CGRectMake(0, 0, 200, 200))
label.numberOfLines = 3
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
let font = UIFont(name: "HelveticaNeue-Bold", size: 20.0)
label.font = font
return label
}()
and then I set the text later in the class
nameLabel.text = name
I've been looking into this for over a day now, and I can honestly say I am completely stumped by why this is not working as I would expect it to.
I'm trying to have a UITableViewCell expand when selected to the correct size based on the UILabel within it. I have used the following code to determine the required size for the UILabel:
extension UILabel {
func requiredHeight() -> CGFloat{
let label:UILabel = UILabel(frame: CGRectMake(0, 0, self.frame.width, CGFloat.max))
label.text = self.text
label.numberOfLines = 0
label.font = self.font
label.sizeToFit()
print("Final Size - \(label.frame.height)")
return label.frame.height + 10
}
}
My issue is, despite this size - when the Cell is resized within the 'heightForRowAtIndexPath' method - it is still not the correct size and the string is being truncated, this can be seen in the below image.
To note - I gather the required size of the cell as soon as the view has loaded and text has been populated into the UILabel.
requiredHeight = overviewLabel.requiredHeight()
if requiredHeight > overviewCell.frame.height {
expander.hidden = false
} else {
expander.hidden = true
}
Any advice on how this could be fixed will be greatly appreciated.
Try this snippet. Just provide exact name of the font and size
Swift 3.x
func requiredHeight() -> CGFloat{
let font = UIFont(name: "Helvetica", size: 16.0)
let label:UILabel = UILabel(frame: CGRect(x:0, y:0, width:200, height:CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = self.text
label.sizeToFit()
return label.frame.height + //Add some space as a part of your bottom and top constraint
}
Swift 2.2
func requiredHeight() -> CGFloat{
let font = UIFont(name: "Helvetica", size: 16.0)
let label:UILabel = UILabel(frame: CGRectMake(0, 0, 200, CGFloat.max))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
label.font = font
label.text = self.text
label.sizeToFit()
return label.frame.height + //Add some space as a part of your bottom and top constraint
}
Suppose your label has top and bottom constraint as 5 and 5 respectively, them make the return statement as
return label.frame.height + 10
NOTE:- Width should be the width you want of the label. It should be according to your UITableView or UIView