I'm trying to change the width of the of a cell's text in the UITableview
Which is taking the width of the container as I can see
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = users[indexPath.row].User
cell.detailTextLabel?.text=String(users[indexPath.row].Score)
cell.textLabel?.bounds.width=20
I would like to show something like ( Truncate Tail):
I think screen short seems you reached the screen width already.so there is no use to increase Label width.If you want to show the full text your textLabel you can follow any one of the below solution.
cell.textLabel?.adjustsFontSizeToFitWidth = true;
it adjust the font size according to the Label width.
cell.textLabel?.numberOfLines = 0
it makes the Label text to display as two lines.
EDIT
if you want truncate tail for the textLabel try this.
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByTruncatingTail
If you are using auto layout you could pin the number on the right to the right edge of the container view, place a horizontal spacer and give the name-label a lower compression resistance priority than the number label. This will make the name label as wide as possible but not so wide that it will clip into the number.
Actually, changing the frame of the textLabel in cell is NOT allowed in TableView. The width of frame for the textlabel of each cell relates to the cell, developer couldn't set it programmatically. However, I worked out a feasible solution: we may not change the frame of the textLabel, but we may change the source text alternatively. If we can truncate the source string in advance, this problem is overcome.
//Assign each element in the array a row in table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var myCell = myTableView.dequeueReusableCell(withIdentifier: "The Cell")
if myCell == nil {
myCell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "The Cell")
}
myCell?.accessoryType = UITableViewCellAccessoryType.none
// If the text is too long (longer than 20 char), the text will be truncated
// The lenght of label cannot be set effectively here, so I truncate the source string alternatively
var myString: String = sectionArray[indexPath.section].items[indexPath.row]
if myString.characters.count > 20 {
let myIndex = myString.index(myString.startIndex, offsetBy: 20)
myString = myString.substring(to: myIndex)
myString += "..."
}
myCell?.textLabel?.text = myString
myCell?.detailTextLabel?.text = "\(NSDate())"
return myCell!
}
With this method, the effect of changing the textLabel's frame could be achieved.
Related
I'm trying to format the position of numbers in a UIViewController's detailTextLabel property. The numbers in the detailTextLabel part of the UIViewTable are too far to the right (as in the image).
I've tried:
cell.detailTextLabel?.textAlignment = .center
but it doesn't work. I've tried .left, .right, .justified and various settings in interface builder.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PracticeWord", for: indexPath)
let sortedPracticeWord = sortedPracticeWords[indexPath.row]
print("practiceWord is: \(sortedPracticeWord)")
let split = sortedPracticeWord.components(separatedBy: "::")
cell.textLabel?.text = split[0]
cell.textLabel?.textColor = UIColor.white
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView!.backgroundColor = UIColor(white: 1, alpha: 0.20)
cell.textLabel?.text = split[1]
cell.detailTextLabel?.text = split[2]
cell.detailTextLabel?.textAlignment = .center
print("cell is: \(cell)")
return cell
}
I would like each number to end just under the 'g' of the word 'wrong'.
I think what's happening here is that the detailTextLabel is being sized to fit the text length, and the whole label aligned to the right edge.
I'd try adding a whitespace to the text that you add to the detail text label.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PracticeWord", for: indexPath)
let sortedPracticeWord = sortedPracticeWords[indexPath.row]
print("practiceWord is: \(sortedPracticeWord)")
let split = sortedPracticeWord.components(separatedBy: "::")
cell.textLabel?.text = split[0]
cell.textLabel?.textColor = UIColor.white
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView!.backgroundColor = UIColor(white: 1, alpha: 0.20)
cell.textLabel?.text = split[1]
cell.detailTextLabel?.text = split[2] + " "
cell.detailTextLabel?.textAlignment = .center
print("cell is: \(cell)")
return cell
}
The detail text label is only displayed with the built in UITableViewCell styles, and it's not going to respect the alignment because the default styles will do their own thing and won't give you that much control. Which is just fine for simple stuff but quickly becomes a limitation for anything nontrivial.
If you want to control placement you'll need to define your own custom table cell with left and right UILabels, and constrain them exactly where you want them. Also bear in mind that if the user changes the system font size, you still may not line up with the 'g' character, so you might want to consider a different design, or just not worrying about that alignment.
See https://developer.apple.com/documentation/uikit/uitableviewcell/cellstyle for a description of the built in styles, but I suspect you'll need to create your own if the default style isn't doing what you want.
After creating custom cells by following this tutorial:
creating custom tableview cells in swift
It seems to be giving me what I originally intended.
I need to programmatically add two different types of cells to my UITableView. The content of these cells is not known until runtime. The first type is an HTML formatted string and the second is an image. There can be any combination of these cells and they can occur in any order.
In the IB, I've set up two prototype cells, one with the id 'htmlCell' and the other 'imageCell'. Here is the relevant code:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "htmlCell")
let thisSection = self.sections[indexPath.row]
if thisSection.type! == "html" {
let htmlString = thisSection.text
let htmlHeight = contentHeights[indexPath.row]
let webView:UIWebView = UIWebView(frame: CGRect(x:0, y:0, width:cell.frame.size.width, height:htmlHeight))
cell.addSubview(webView)
webView.tag = indexPath.row
webView.scrollView.isScrollEnabled = false
webView.isUserInteractionEnabled = false
webView.delegate = self
webView.loadHTMLString(htmlString!, baseURL: nil)
} else if thisSection.type! == "image" {
cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "imageCell")
let imageName = "logo"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width-20, height: 200)
cell.addSubview(imageView)
return cell
}
return cell
}
func webViewDidFinishLoad(_ webView: UIWebView) {
if (contentHeights[webView.tag] != 0.0) {
// we already know height, no need to reload cell
return
}
let strHeight = webView.stringByEvaluatingJavaScript(from: "document.body.scrollHeight")
contentHeights[webView.tag] = CGFloat(Float(strHeight!)!)
tableView.reloadRows(at: [NSIndexPath(item: webView.tag, section: 0) as IndexPath], with: UITableViewRowAnimation.automatic)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.contentHeights[indexPath.row]
}
The content loads, but it's a bit of mess. Here is a screenshot to provide an example. In this case, the image should appear between the two webviews. But as you can see, the image lands directly on top of the second webview, with the two top borders aligned.
Also, when I click on the image, it completely disappears. I'm assuming that it goes behind the second webview. Just a guess.
The CGRects that I am using have somewhat arbitrary widths and heights at the moment. I suspect that this is where part of the issue lies. Finally, the webViewDidFinishLoad always returns a height of 667.0 regardless of the true height of the webview content.
Anyone know how I can make these views land in the correct sequence? Thank you.
There are many potential issues here, let's tackle a few of them and see if that get's you going :)
If you defined prototype cells in IB and your tableView is properly linked to the xib or storyboard file, then you should dequeue cells instead of construct them.
Try this:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell //declare but don't assign yet. Use let because after we assign once, we wont be reassigning.
...
if thisSection.type! == "html" {
cell = tableView.dequeueReusableCell(withIdentifier: "htmlCell", for: indexPath)
...
} else if thisSection.type! == "image" {
cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath)
...
}
return cell
}
Notice, we declare and return cell at the top level scope and assign the cell only when we know what type it will be. Now that you are dequeueing, the cells will be reused when appropriate and reduce the number of allocation made when a user scrolls making your app zippier.
Next, since you are adding subviews to your cells, you'll need to clear the subviews each time you dequeue a cell. This requires a bit of additional code but will make things a lot cleaner in the long run.
After each call to dequeueReusableCell... add:
cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
So far so good. You've got the cells dequeuing cleanly, but now we need to make sure we setup the cells properly.
First, the html cell:
Remove the line:
let htmlHeight = contentHeights[indexPath.row]
The tableView is ultimately responsible for sizing the cells and the content will take up the full size of the cell. Replace the next 2 lines with this:
let webView = UIWebView(frame: cell.contentView.bounds)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
cell.contentView.addSubview(webView)
The autoresizing mask makes sure that if the cell's content view size changes, the webview will be resized as well. Since the content view is managed by iOS, the webview will always be the width of the tableview and the height will be whatever you provide in the delegate method heightForRow. Notice also, we never add subviews directly on to the cell, always on to it's contentView.
Next, we'll do the same for the imageCell:
let image = UIImage(named: "logo")
let imageView = UIImageView(frame: cell.contentView.bounds)
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.contentMode = .aspectFit //this makes sure the image take ups the full size of the image view, but no more. and this keeps the image's aspect ratio. Adjust this to other content modes if necessary.
imageView.image = image
cell.contentView.addSubview(imageView)
Ok, now we have cells that are being setup and leaving the sizing out of the equation. So let's get sizing working in the right place:
The biggest challenge that you'll face in building this is getting the cell heights right. Since the images are in your bundle, you'll know their size as soon as you instantiate the UIImage, however we don't want to do this more than absolutely necessary. You might want to come up with a more performant way of doing this later, but for now we'll create a UIImage and get it's height:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if thisSection.type! == "html" {
return self.contentHeights[indexPath.row]
} else if thisSection.type! == "image" {
let image = UIImage(named: "logo")
let imageHeight = (image.size.height * tableView.bounds.size.width) / image.size.width // The proportional size of the image stretched to the table view's width
return imageHeight
}
}
Your method for calculating the web view height and reloading cell might work, but it might appear clunky to the user. My suggestion at this point would be to return a fixed height for the web content. When the user taps a web cell, you can push a new view controller with a web view that can display all the content. Otherwise you can try having all of your html generate web content of roughly the same size.
Ok, this was long but hopefully gets you off to a good start :) I'm happy to follow up if you need more clarification.
I have a custom table cell inside a table view, which contains a horizontal stack view of image view and text field... The image view mode is ScaleAspectFit
When the view loads initially, the views are arranged as they should be, but when a cell is touched or the table view is scrolled, the image view resizes unexpectedly
Here are screenshots and my code
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ImagesTableViewCell", forIndexPath: indexPath) as! ImagesTableViewCell
// Configure the cell...
let pic = self.patient!.images[indexPath.row]
cell.imageViewPic.clipsToBounds = true
cell.imageViewPic.contentMode = .ScaleAspectFit
cell.imageViewPic.image = getImage(pic.imagePath)
cell.labelPic.text = pic.caption
cell.imageViewPic.tag = indexPath.row
cell.labelPic.tag = indexPath.row
return cell
}
I tried all Content Modes with no benefit
Edit2: here is getImage() function code as requested
func getImage(filename: String) -> UIImage {
let path = getDocumentsDirectory().stringByAppendingPathComponent(filename)
let image = UIImage(contentsOfFile: path)
return image!
}
Set the clipsToBounds property of UIImageView to true.
Like
set cell.imageViewPic.clipsToBounds=true
Also check the constraint of the imageview. It should be center vertical align,leading, fixed width and fixed height.
try below steps it may can help you.
render ImageView with fixe width and height
make clipSubviews = YES. in story board OR cell.imageViewPic.clipsToBounds = YES;
I'm an Android developer, recently started to discover iOS using SWIFT 2
I would like to have a dynamic layout in my custom cell, I have an UImageView. and I have two cases :
To show the image.
To hide the image (OR to remove it) the Cell height should change
I've made my cell height dynamic using :
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
How Can I achieve that ! here is my code :
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! PostCell
if (posts[indexPath.row].PostImageURL == "None") {
// Remove the Image
} else {
cell.PostImage.load(posts[indexPath.row].PostImageURL!)
}
You could try to set the UIImageView's Outlet to be hidden.
You could try to set the UIImageView to nil
You could try to loop through the sub idea of your Cell and remove the UIImageView from the View Hierarchy.
Hope I could help!
I would like to hide some elements in a custom cell when we overpass a specific number of row. I added more row than the ones visible, because I needed to scroll until the last row without the bouncing effect. But now I have more cells, and I don't need the cells after row > 13.
I tried to setNeedsDisplay the cell with a if else, but the dequeue... method has a bad effect on the cells, when I scroll up, back to the previous cells, they don't have the texts anymore, like the row > 13. Is there a way to use the dequeue method, and let the content for the rows < 13, and remove the content for the rows > 13 ?
Here is some code :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var identifier = ""
if tableView == self.tableView{
identifier = "MyCell"
let cell = tableView.dequeueReusableCellWithIdentifier(identifier) as MyCell
if indexPath.row < 14 {
cell.showContent = true
cell.label.text = "test\(indexPath.row)"
}
else {
cell.showContent = false
cell.label.text = ""
cell.addItem.text = ""
}
cell.setNeedsDisplay()
return cell
}
//MyCell
override func drawRect(rect: CGRect) {
if !showContent {
label.text = ""
addItem.text = ""
}
else {
let path = UIBezierPath()//custom separator that should not be drawn for row > 13
Thanks
You shouldn't modify the text this way in drawRect. You already modified the labels in cellForRow. That's all you need.
That said, this isn't how I would do it. I'd probably create a different cell with its own identifier for empty cells. That way they can be really simple and you don't have to do things like cell.setNeedsDisplay() to get rid of the separator line. So in cellForRow, just return one kind of cell for data rows, and a different kind of cell for empty rows. There's no rule that says all the cells have to be the same class.