Hello I'm trying to make the table view with custom cell. The main goal of making it is that I resize the height of tableview cell in proportion to label height which is changed by the size of text. when I run my app with below code, it looks go well. However getStringHeight methods sometimes don't recognize the line of the cell. Even if the number of line is just one, that method allocates the two line of cell which makes the cell unnecessary space.
I think the if it would recognizes two lines, line is over 3/4 filled with text in one line. I think I use Korean font included in the Apple SD Gothic (Family). But I can enter the exact name into UIFont(name: "", size: ) because it doesn't recognize it.
Can you help me? it's so important project. I stayed up all night but I haven't come up with idea to fix this problems.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
let rowNum = downloadedContents!.count - indexPath.row - 1
let sizeFactory = UINib(nibName: "DownLoadPlayViewControllerCell", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as? DownLoadPlayViewControllerCell
sizeFactory!.frame.size.height = 300
var bounds = CGSize(width: tableView.bounds.width, height: 78.5)
let pod = self.pods[rowNum]
if(sizeFactory != nil)
{
let top = sizeFactory?.programLabel.frame
let bottom = sizeFactory?.dateFilesizeLabel.frame
var labelSize = getStringHeight(pod.contentTitle!, fontSize: 13.0, width: sizeFactory!.titleLabel.frame.width)
let totalCellHeight = labelSize + top!.height + bottom!.height + 28.0
bounds.height = totalCellHeight
}
return bounds.height
}
func getStringHeight(mytext: String, fontSize: CGFloat, width: CGFloat) -> CGFloat
{
let font = UIFont(name: "Apple SD Gothic Neo", size: fontSize)
let size = CGSizeMake(width,CGFloat.max)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .ByWordWrapping;
let attributes = [NSFontAttributeName:font! as UIFont,
NSParagraphStyleAttributeName:paragraphStyle.copy()]
let text = mytext as NSString
let rect = text.boundingRectWithSize(size, options:.UsesLineFragmentOrigin, attributes: attributes, context:nil)
return rect.size.height
}
I may suggest that you use:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 60
This will autosize the height of the cell without looking for the height of the text.
You can put these two lines on viewDidLoad as for the estimated row height, it helps you out in rendering.
Try this code:
Note: Both method should work.Tested in Swift 3.
Method 1:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
tableView.estimatedRowHeight = 44.0 // standard tableViewCell height
tableView.rowHeight = UITableViewAutomaticDimension
return yourArrayName.count
}
Method 2:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Note: You may need to put this code inside your cellForRowAt
yourCellName.sizeToFit()
Related
Have been trying to change the font size inside the UITableViewCell dynamically based on the device size.
Code to create a table inside a UIView (in an SKScene):
suitsView = UIView()
suitsView.backgroundColor = .purple
suitsView.alpha = 0
self.scene?.view?.addSubview(suitsView)
suitsTableView.register(suitsTableCell.self, forCellReuseIdentifier: "cellSuits")
suitsTableView.separatorColor = UIColor(red: 153/255, green: 255/255, blue: 153/255, alpha: 1)
suitsTableView.tableFooterView = UIView()
suitsTableView.allowsMultipleSelection = true
suitsTableView.reloadData()
suitsTableView.alpha = 0
suitsTableView.populateTable()
displayText() // Displays Text
suitsTableView.rowHeight = UITableViewAutomaticDimension
suitsTableView.estimatedRowHeight = 600
suitsView.addSubview(suitsTableView)
This is my UITableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
let cell : SuitsTableCell = tableView.dequeueReusableCell(withIdentifier: "cellSuits", for: indexPath as IndexPath) as! SuitsTableCell
cell.lblName.text = self.items[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return self.bounds.height/5
}
This is my UITableViewCell:
override init(style: UITableViewCellStyle, reuseIdentifier: String?)
{
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = UITableViewCellSelectionStyle.none
backgroundColor = UIColor.yellow
contentView.backgroundColor = UIColor.yellow
// Font size
var fontSize : CGFloat = 20.0
let marginGuide = contentView.layoutMarginsGuide
lblName = UILabel()
lblName.textColor = UIColor.black
lblName.font = UIFont(name:"Noteworthy-Bold", size: fontSize)
// CONSTRAINTS
lblName.translatesAutoresizingMaskIntoConstraints = false
lblName.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
lblName.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
lblName.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
lblName.widthAnchor.constraint(equalToConstant: 200)
lblName.numberOfLines = 0
lblName.adjustsFontSizeToFitWidth = true
lblName.contentScaleFactor = 0.5
contentView.addSubview(lblName)
}
On iPhone 8, my table looks like this:
But on iPad, it looks like this:
I need to have a bigger font in iPad. How can I adjust the font size based on constraints or the row height?
Thanks.
Edit: I am not using Storyboard; please do not suggest it.
Interestingly, if I have this code inside my cellForRow method:
cell.lblName.font = UIFont(name:"Noteworthy-Bold", size: (tableView.bounds.height/14).rounded())
I can adjust the font size but the first row (None) is always empty...
You can use like this:
// Screen width.
public var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
// Screen height.
public var screenHeight: CGFloat {
return UIScreen.main.bounds.height
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
let cell : SuitsTableCell = tableView.dequeueReusableCell(withIdentifier: "cellSuits", for: indexPath as IndexPath) as! SuitsTableCell
cell.lblName.text = self.items[indexPath.row]
cell.lblName.font = UIFont(name:"Noteworthy-Bold", size: (screenwidth * 0.0373).rounded()) // 0.0373 = requiredfontsize / 375
return cell
}
Note: I don't know whether this method is right or wrong but I have tried this and it worked for me.
You can go through this for more info: Scale text label by screen size
Use Size-Class and hit on + symbol just before the Font tab (in storyboard) and choose Regular * Regular and set the font size as per your wish.
I'm making a chat like application, where the tableView displays dynamic height cells.
The cells have their views&subviews constrained in the right way
So that the AutoLayout can predict the height of the cells
(Top, Bottom, Leading, Trailing)
But still - as you can see in the video - the scroll indicator bar shows that wrong heights were calculated:
It recalculates the heights when a new row is appearing.
Video: https://youtu.be/5ydA5yV2O-Q
(On the second attempt to scroll down everything is fine)
Code:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
It is a simple problem. Can someone help me out?
Update 1.0
Added github:
https://github.com/krptia/Test
But still - as you can see in the video - the scroll indicator bar shows that wrong heights were calculated:
So what you want is precise content height.
For that purpose, you cannot use static estimatedRowHeight.
You should implement more correct estimation like below.
...
var sampleCell: WorldMessageCell?
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "WorldMessageCell", bundle: nil), forCellReuseIdentifier: "WorldMessageCell")
sampleCell = UINib(nibName: "WorldMessageCell", bundle: nil).instantiate(withOwner: WorldMessageCell.self, options: nil)[0] as? WorldMessageCell
}
...
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if let cell = sampleCell {
let text = self.textForRowAt(indexPath)
// note: this is because of "constrain to margins", which value is actually set after estimation. Do not use them to remove below
let margin = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
// without "constrain to margins"
// let margin = cell.contentView.layoutMargins
let maxSize = CGSize(width: tableView.frame.size.width - margin.left - margin.right,
height: CGFloat.greatestFiniteMagnitude)
let attributes: [NSAttributedString.Key: Any]? = [NSAttributedString.Key.font: cell.messageLabel.font]
let size: CGRect = (text as NSString).boundingRect(with: maxSize,
options: [.usesLineFragmentOrigin], attributes: attributes, context: nil)
return size.height + margin.top + margin.bottom
}
return 100
}
This is too precise (actually real row height) and maybe slow, but you can do more approximate estimation for optimization.
You need to set tableFooterView to empty.
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
// your staff
}
According to your answer on my comment that when you set
estimatedHeightForRowAt and heightForRowAt the same values it does
work
I can confirm that you are right and that there is the problem that AutoLayout cannot calculate the right value for estimatedHeightForRowAt. So basically there are two possible things to do:
find alternative layout that will produce better results
make your own calculation for estimatedHeightForRowAt which will produce more accurate results (in general you should be able to tell what is expected height per text length and then add margins to that figure - you need to put a bit of effort to find the proper math, but it should work).
The problem is with your estimatedHeightForRowAt method. As the name implies it gives the estimated height to the table so that it can have some idea about the scrollable content until the actual content will be displayed. The more accurate value will result in a more smooth scrolling and height estimation.
You should set this value to big enough so that it can represent the height of your cell with the maximum content. In your case 650 is working fine.
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 650
}
The result would be far better with this approach.
Also, there is no need to implement delegate method for height until you want a variation on index bases. You can simply set table view property.
tableView.estimatedRowHeight = 650.0
tableView.rowHeight = .automaticDimension
Optimization
One more thing I noticed in your demo project. You've used too many if-else in your cellForRowAtIndexPath which is making it little slower. Try to minimize that. I've done some refinement to this, and it improves the performance.
Define an array which holds your message text.
var messages = ["Lorem ipsum,"many more",.....]
Replace your cellForRowAt indexPath with below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell : WorldMessageCell
cell = tableView.dequeueReusableCell(withIdentifier: "WorldMessageCell", for: indexPath) as! WorldMessageCell
if indexPath.row < 14 {
cell.messageLabel.text = messages[indexPath.row]
}
else if indexPath.row >= 14 && indexPath.row != 27 {
cell.messageLabel.text = messages[14]
}
else if indexPath.row == 27 {
cell.messageLabel.text = messages.last
}
return cell
}
Just remove highlighted view from UITableView and it's work like a charm.
Hope it helps.
This is expected behaviour when using coarse cell height estimates (or not providing them at all, as you do). The actual height is computed only when the cells come on screen, so the travel of the scroll bar is adjusted at that time. Expect jumpy insertion/deletion animations too, if you use them.
I hope you heard about this a lot. so take short break and come back on desk and apply 2 - 3 steps for step this.
1) Make sure Autolayouts of label of Cell is setup correct like below.
2) UILabel's number of lines set zero for dynamic height of text.
3) setup automatic dimension height of cell.
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
and I believe its should be work. see results of my code.
why you add view in table view , it can also work without it. I just delete that view and change some constraints(like bottom constraints change safe area to superview) , and it works fine.
see this video
download storyboard and add it to your project and then check
Configure your tableview with these in viewDidLoad()
tableView.estimatedRowHeight = 100.0
tableView.rowHeight = UITableView.automaticDimension
tableView.tableFooterView = UIView()
And you should remove both height datasource method.
What you want to do is eliminate the extra blank cells. You can do so by setting the tableFooterView to an empty UIView in the viewDidLoad method. I cloned the code from your GitHub and revised the method:
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "WorldMessageCell", bundle: nil), forCellReuseIdentifier: "WorldMessageCell")
tableView.tableFooterView = UIView()
}
setting the tableFooterView to nil worked for me as well
tableView.tableFooterView = nil
The problem I am facing ishat i am unable to update the variable lin. I have tried multipleimes a f:) . To get to the relevode quick 2nd line in the code is where the variable is identified and then if u scroll to bottom of the code that is where the variable is suppose to change but does not
let menable = UITabletgView()
let arfSorces:[String] = ["C"]
public func runMenu(){
if let window = UIApplication.sharedApplication().keyWindow{
dimming.frame = window.frame
dimming.backgroundColor = UIColor(white: 0.9, alpha: 0.5087)
dimming.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector("eefefef")))
let height: CGFloat = 100000
let y = window.frame.height - height + height
menuTable.frame = CGRect(x: 10, y: window.frame.height, width: window.frame.height, height: height)
window.addSubview(dimming)
window.addSubview(mee)
eView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("c", forIndexPath: indexPath)as UITableViewCell
e.textLabel?.text = ars[indexPath.item]
return e
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 50000
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
func RunLink(){
if let v = c{
98bdfe8daf6543d92"
Inside the if statement you declare a brand-new variable link1 that only exists inside that if statement. Change
if arrayOfSorces[indexPath.item] == arrayOfSorces[0]{
var link1 = "https://newsapi.org/v2/everything?sources=bbc-news&apiKey=a9ea5ee627044bd98bdfe8daf6543d92"
RunLink()
}
To
if arrayOfSorces[indexPath.item] == arrayOfSorces[0]{
link1 = "https://newsapi.org/v2/everything?sources=bbc-news&apiKey=a9ea5ee627044bd98bdfe8daf6543d92"
RunLink()
}
(Get rid of the var. That creates a new variable rather than changing the value that's in the existing variable.)
I'm using Swift 3. I've already made this chat bubbles, but I want it to change sizes...
for example, when there 4 lines of text it's not changing size (making bigger), and I want it to do automatically. How can I do it?
Thank You!
You need to add constraints in textview/label like this
Observe constraints of textview selected ,its top, bottom, right, left and Height >= 30 only this will work
and tableview
tableview.estimatedRowHeight = 44.0
tableview.rowHeight = UITableViewAutomaticDimension
So it will work like this
Note: Above label and Date have fixed height. Alternate you can Play with content hugging priority respectives.
Try this code.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return estimateFrameForText(text: YourMessageText, width: 280).height + 32
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell
bubbleViewWidthConstraint.constant = estimateFrameForText(text: YourMessageText, width: 280).width + 32
return cell
}
private func estimateFrameForText(text: String, width: CGFloat) -> CGRect {
let size = CGSize(width: width, height: 10000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 13)], context: nil)
}
You need to set the font in this code and the font in your UI as same.
I want to dynamically change the height of the cell such that the image fills the imageView in the width and the image maintains the aspect ratio.
I am trying to do this using UITableViewAutomaticDimension. Based on tutorials available online, this seemed to be a straightforward task. But in my case, the height of the cell doesn't adjust to perform this task. Either the image fills with aspect ratio leaving a lot of width space empty or fills the width but clips the part of the image vertically. I made sure there are leading, trailing, top and bottom constraints with respect to the cell view and also the tableView.rowHeight = UITableViewAutomaticDimension and table.estimatedRowHeight is set in viewDidLoad. I have uploaded the project at this link. Could someone please suggest what could I be missing here?
Link to GitHub
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var picTableView: UITableView!
let reuseidentifier = "dynamicCellTableViewCell"
let images = [UIImage(named: "feedImage1"), UIImage(named: "feedImage2"), UIImage(named: "feedImage3")]
override func viewDidLoad() {
super.viewDidLoad()
picTableView.rowHeight = UITableViewAutomaticDimension
picTableView.estimatedRowHeight = 1000
}
// MARK: Table View Data Source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return images.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseidentifier, forIndexPath: indexPath) as! DynamicCellTableViewCell
cell.displayImageView.image = images[indexPath.row]
return cell
}
}
I figured the solution. UIImageView when used with UITableViewAutomaticDimension only looks for the UIImage's true width and height as the size. So UIImageView adjusts its height to respond to UIImage's original height and not the scaled height for scaled width (aspectFit) to match layout constraint.
So trick is to first change the width and height of the image to match the width and height that you need in the cell. Rest of the job will be done by UITableViewAutomaticDimension. Below is the updated cellForRowAtIndex function that works now. Thanks all for giving your inputs.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseidentifier, forIndexPath: indexPath) as! DynamicCellTableViewCell
let image = images[indexPath.row]!
let newWidth = cell.displayImageView.frame.width
let scale = newWidth/image.size.width
let newHeight = image.size.height * scale
UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
cell.displayImageView.image = newImage
return cell
}
You need implement next method for UITableViewController:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 1000.0;
}
Inside the method, you must know what dimension will have each cell. You must know in advance what height will have each cell.
For example, you could create an array of CGFloat saving the height will have each row, and when height change, you can update this position in array and execude tableView.reloadData().
var arrayHeight:CGFloat?
//Now fill array with height of each cell
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return arrayHeight[indexPath.row];
}
cell.updateConstraintsIfNeeded()
cell.setNeedsUpdateConstraints()
add these two lines in the cellForRowAtIndexPath delegate method
And make sure that top, bottom, right, left, constraints are set in the storyboard