UITableView - adjustsFontSizeToFitWidth in sectionHeader - ios

I've created a custom sectionHeader for my UITableview.
Sometimes I have to fill it with large text, so I added adjustsFontSizeToFitWidth = true which normally works.
In this case the font is not resized when it is to large. Does anyone have an idea what I'm doing wrong?
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.lightGray
let headerLabel = UILabel(frame: CGRect(x: 30, y: 0, width:
tableView.bounds.size.width, height: tableView.bounds.size.height))
headerLabel.textColor = UIColor.white
headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)
headerLabel.sizeToFit()
headerLabel.adjustsFontSizeToFitWidth = true
headerLabel.clipsToBounds=true
headerLabel.numberOfLines=0
headerLabel.lineBreakMode = NSLineBreakMode.byTruncatingTail
headerLabel.minimumScaleFactor = 0.4
headerView.addSubview(headerLabel)
return headerView
}

You need to remove
headerLabel.sizeToFit() to make adjustsFontSizeToFitWidth property working.

The solution was to delete this line:
headerLabel.sizeToFit()

Related

How to change the Title label text alignment of the section when it was present

I'm loading the UITableView with two section and I've used viewForHeaderInSection method to set the title for each section. Initially I've set the title label text alignment as .left. Now when I scroll the table view and the second section was present i want to change the title label text alignment to .center. And when I scroll down the table view i want to show as initial(like second section text alignment as .left). How can I Do that?
Here is the TableView Delegatemethod I've used to load the section.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 20))
headerLabel.textColor = UIColor.white
headerLabel.textAlignment = .left
headerLabel.font = Fonts.kFontWith_14
if section == 0
{
headerLabel.text = NSLocalizedString("Suggested", comment: "")
headerLabel.backgroundColor = Colors.kAppBgColor
headerLabel.textAlignment = .left
}
else{
headerLabel.text = NSLocalizedString("Contacts", comment: "")
headerLabel.backgroundColor = Colors.kLightBlueColor
headerLabel.textAlignment = .left
}
headerLabel.sizeToFit()
return headerLabel
}
Take one bool variable on global, The follwoing delegate will respond you when scroll happens
var isDirection = Bool()
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if velocity.y > 0 {
print("up")
isDirection = true
}
if velocity.y < 0 {
print("down")
isDirection = false
}
}
Then write a condition in respective `tableview` delegate
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 20))
headerLabel.textColor = UIColor.white
headerLabel.textAlignment = .left
headerLabel.font = Fonts.kFontWith_14
if section == 0
{
headerLabel.text = NSLocalizedString("Suggested", comment: "")
headerLabel.backgroundColor = Colors.kAppBgColor
}
else{
headerLabel.text = NSLocalizedString("Contacts", comment: "")
headerLabel.backgroundColor = Colors.kLightBlueColor
}
if isDirection{
headerLabel.textAlignment = .left
}else{
headerLabel.textAlignment = .center
}
return headerLabel
}
Remove
headerLabel.sizeToFit()
This is sample, you enable bool variable based on your scroll direction. If delegate does not call, reload your table on scrollViewWillEndDragging
Here is the way for my problem
Take a bool value and a CGPoint globally and assign the UITableView.ContentOffset value to the defaultOffset in viewDidload Method.
var isScroll:Bool = Bool()
var defaultOffSet = CGPoint()
override func viewDidLoad() {
super.viewDidLoad()
defaultOffSet = tableView.contentOffset
}
And I've used this scrollView delegate method
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if contactsTableView.rectForHeader(inSection: 1).origin.y > contactsTableView.contentOffset.y-defaultOffSet.y {
isScrolled = false
}
else if tableView.rectForHeader(inSection: 0).origin.y <= tableView.contentOffset.y-defaultOffSet.y{
isScrolled = true
}
tableView.reloadData()
}
And In my tableview viewForHeaderInSection
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 20))
headerLabel.textColor = UIColor.white
headerLabel.textAlignment = .left
headerLabel.font = Fonts.kFontWith_14
if section == 0
{
headerLabel.text = NSLocalizedString("Suggested", comment: "")
headerLabel.backgroundColor = Colors.kAppBgColor
headerLabel.textAlignment = .left
}
else{
headerLabel.text = NSLocalizedString("Contacts", comment: "")
headerLabel.backgroundColor = Colors.kLightBlueColor
if isScrolled{
headerLabel.textAlignment = .center
headerLabel.backgroundColor = Colors.kAppBgColor
}
}
}
}

Programmatically adding constraints to a UITableView header

I have a header on my tableview, which has an image and a label. I want to center the UIImage and have the label pinned underneath it. This is my code currently:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerLabel = UILabel()
let logoView = UIImageView()
headerLabel.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 21)
headerLabel.text = self.navigationItem.title
headerLabel.textColor = UIColor.white
headerLabel.backgroundColor = UIColor.clear
headerLabel.textAlignment = NSTextAlignment.center
logoView.frame = CGRect(x: 0, y: 0, width: 90, height: 90)
let logo: UIImage = UIImage(named: self.navigationItem.title!)!
logoView.image = logo
logoView.contentMode = .scaleAspectFill
view.addSubview(headerLabel)
view.addSubview(logoView)
return topView
}
This puts the label centered on the top of the header, and the logo in the top left corner. How can I add constraints (programmatically, no storyboard) to center the image and pink the label below it? I've been using programmatic constraints quite a bit (i.e. something.leftAnchor.constraint(equalTo....) but I'm not sure how to apply it in this situation as it's my first use of a header.
I want to center the UIImage and have the label pinned underneath it.
This can be achieved by making the framing logic of each subview dependent of the neighbouring views. Whilst doing this programmatically, one has to be extra careful about the geometric calculations involved.
This snippet should do it:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let logoView = UIImageView()
logoView.frame = CGRect(x: tableView.center.x - 90/2, y: 0, width: 90, height: 90)
let logo: UIImage = UIImage(named: self.navigationItem.title!)!
logoView.image = logo
logoView.contentMode = .scaleAspectFill
view.addSubview(logoView)
let headerLabel = UILabel()
headerLabel.frame = CGRect(x: 0, y: logoView.frame.size.height, width: view.frame.width, height: 21)
headerLabel.text = self.navigationItem.title
headerLabel.textColor = UIColor.white
headerLabel.backgroundColor = UIColor.clear
headerLabel.textAlignment = NSTextAlignment.center
view.addSubview(headerLabel)
return topView
}
You can use that code to set default paddings.
if #available(iOS 15.0, *) { tableView.sectionHeaderTopPadding = 0 }

Swift Add Footer View In UITableView

This is actually a tableview and tableview cell, and i wanted to add a Submit button after the end of the tableview cell, but how do i do it?
I tried to do it at the storyboard add the button manually, but its not working, the button is not showing. Is there any other way to do it?
I wanted to do like the screenshot below.
Using StoryBoard
In UITableView You can drag UIView, it will set as FooterView if you have more then 0 prototype cell. After Drag you can see it in table view hierarchy as a subview. Now, you can add the label button on that View, you can also set IBAction into ViewController Class File.
Programmatically
Follow 3 Steps
Make one custom view with button,
Swift 3.X / Swift 4.X
let customView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
customView.backgroundColor = UIColor.red
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.setTitle("Submit", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
customView.addSubview(button)
Swift 2.X
let customView = UIView(frame: CGRectMake(0, 0, 200, 50))
customView.backgroundColor = UIColor.redColor()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.setTitle("Submit", forState: .Normal)
button.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
customView.addSubview(button)
Add that view in Table Footer View.
Swift 2.X/Swift 3.X/Swift 4.X
myTblView.tableFooterView = customView
you can do action on that button in same class.
Swift 3.X/Swift 4.X
#objc func buttonAction(_ sender: UIButton!) {
print("Button tapped")
}
Swift 2.X
func buttonAction(sender: UIButton!) {
print("Button tapped")
}
Swift 3/4
1. If you want to add Table footer which is only visible at the end of TabelView
let customView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 150))
customView.backgroundColor = UIColor.clear
let titleLabel = UILabel(frame: CGRect(x:10,y: 5 ,width:customView.frame.width,height:150))
titleLabel.numberOfLines = 0;
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.backgroundColor = UIColor.clear
titleLabel.textColor = PTConstants.colors.darkGray
titleLabel.font = UIFont(name: "Montserrat-Regular", size: 12)
titleLabel.text = "Payment will be charged to your Apple ID account at the confirmation of purchase. Subscription automatically renews unless it is canceled at least 24 hours before the end of the current period. Your account will be charged for renewal within 24 hours prior to the end of the current period. You can manage and cancel your subscriptions by going to your account settings on the App Store after purchase."
customView.addSubview(titleLabel)
tableView.tableFooterView = customView
2. If you want to add section footer which is visible while scrolling through section.
Adopt UITableViewDelegate and implement following delegate method.
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let vw = UIView()
vw.backgroundColor = UIColor.clear
let titleLabel = UILabel(frame: CGRect(x:10,y: 5 ,width:350,height:150))
titleLabel.numberOfLines = 0;
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.backgroundColor = UIColor.clear
titleLabel.font = UIFont(name: "Montserrat-Regular", size: 12)
titleLabel.text = "Footer text here"
vw.addSubview(titleLabel)
return vw
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 50))
return footerView
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 50
}
Add UILabel as UITableView.Footer in swift
let footerView:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width:320 , height: 500))
footerView.text = "add description ro Timevc in order user will see the result of lesson time"
footerView.numberOfLines = 0;
footerView.sizeToFit()
tableView.tableFooterView = footerView
tableView.contentInset = (UIEdgeInsetsMake(0, 8, -footerView.frame.size.height, 8))
// for Header and footer
// for header
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let HeaderView = UIView(frame: CGRect(x: 0, y: 0, width: TableViews.frame.size.width, height: 10))
HeaderView.backgroundColor = .green
let HeaderTitle = UILabel(frame: CGRect(x: 10, y: 10, width: TableViews.frame.size.width, height: 20))
HeaderTitle.text = "Hi You Welcome"
HeaderView.addSubview(HeaderTitle)
return HeaderView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 40 }
// for footer
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = UIView()
footer.backgroundColor = UIColor.green
let titleLabel = UILabel(frame: CGRect(x: 10, y: 5, width: 350, height: 30))
titleLabel.textColor = .blue
titleLabel.text = "Hi I am Muhammad Hassan"
footer.addSubview(titleLabel)
return footer
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 40 }
... this is the best function for the header and footer ...
You can use
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 16, right: 0)

Add subview on tableView cells causes a sad patchwork

I want to give a chat aspect to a table view of messages in my iPhone app.
In order to perform a quality render I add two subviews: the text and the "container" which is just a view with background color.
Even if it works the first time, when I scroll, it becomes really messy because it keeps adding lots of subviews.
Here you can see it when clean
And then when it has become messy
Here is the function to handle the transform, it's called when scrolling.
func configChatCell(cell: UITableViewCell, text: String, color:UIColor)
{
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
cell.textLabel?.textColor = UIColor.whiteColor()
let fixedWidth = cell.bounds.width - 150
let textView: UITextView = UITextView(frame: CGRect(x: 0, y: 0, width: 10, height: CGFloat.max))
textView.text = text
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max))
var newFrame = textView.frame
newFrame.size = CGSize(width: min(newSize.width, fixedWidth), height: newSize.height)
textView.sizeThatFits(newFrame.size)
textView.frame = newFrame
textView.backgroundColor = UIColor.clearColor()
self.rowHeight = textView.frame.height+20
let view = UIView()
view.backgroundColor = color
print(textView.frame.height+10)
view.frame = CGRect(x: 0, y: 5, width: textView.frame.width+50, height: textView.frame.height+10)
view.layer.cornerRadius = 5
cell.backgroundColor = UIColor.clearColor()
cell.contentView.addSubview(view)
cell.contentView.addSubview(textView)
cell.contentView.sendSubviewToBack(view)
}
If I remove the subviews each time I scroll, nothing appears on screen.
Can somebody help me to find a solution? Or is there any other way to do this?
Thanks in advance!
I quickly wrote up something for this.
It starts with the ChatCell
class ChatCell: UITableViewCell {
var messageLabel: UILabel? {
didSet {
messageLabel?.text = message
}
}
var message: String? {
didSet {
messageLabel?.text = message
}
}
class func messageCell(withText text: String, leading: Bool = true) -> ChatCell {
let cell = ChatCell()
cell.message = text
// Make the container
let container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(container)
container.topAnchor.constraintEqualToAnchor(cell.contentView.topAnchor, constant: 8).active = true
container.bottomAnchor.constraintEqualToAnchor(cell.contentView.bottomAnchor, constant: -8).active = true
if leading {
container.leadingAnchor.constraintEqualToAnchor(cell.contentView.leadingAnchor, constant: leading ? 8 : 8*8).active = true
container.trailingAnchor.constraintLessThanOrEqualToAnchor(cell.contentView.trailingAnchor, constant: leading ? -8*8 : -8).active = true
} else {
container.leadingAnchor.constraintGreaterThanOrEqualToAnchor(cell.contentView.leadingAnchor, constant: leading ? 8 : 8*8).active = true
container.trailingAnchor.constraintEqualToAnchor(cell.contentView.trailingAnchor, constant: leading ? -8*8 : -8).active = true
}
// Make the messageLabel.
let messageLabel = UILabel()
messageLabel.numberOfLines = 0
messageLabel.textColor = UIColor.whiteColor()
messageLabel.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(messageLabel)
// Add constraints.
messageLabel.topAnchor.constraintEqualToAnchor(container.topAnchor, constant: 8).active = true
messageLabel.bottomAnchor.constraintEqualToAnchor(container.bottomAnchor, constant: -8).active = true
messageLabel.leadingAnchor.constraintEqualToAnchor(container.leadingAnchor, constant: 8).active = true
messageLabel.trailingAnchor.constraintEqualToAnchor(container.trailingAnchor, constant: -8).active = true
cell.messageLabel = messageLabel
container.backgroundColor = UIColor(red:0.19, green:0.70, blue:1.00, alpha:1.00)
container.layer.cornerRadius = 12.0
return cell
}
}
The cell also includes support for leading and trailing messages, for back and forth conversation. Perhaps make an array of tuples like this:
let messages: [(message: String, leading: Bool)] = [("Hello", true), ("My name is John Doe and this works quite well", false), ("I would agree", true)]
Then in your tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell you could do this:
let cell = ChatCell.messageCell(withText: messages[indexPath.row].message, leading: messages[indexPath.row].leading)
return cell
Let me know if this works for you. I tested it in a Playground and it works as expected
Assuming that your configureChatCell is called from tableView:cellForRowAtIndexPath:, then #Paulw11 is right; cells are reused, so you should only make changes that are unique to that row in the table. In your example, the only calls that you should be making in your method are textView.text = text and the ones to resize the textView to fit. Everything else should go in a dynamic cell prototype in the storyboard or, if you want to do everything in code (which I have a bad feeling you do), then put the rest of the configuration in a UITableViewCell subclass, then register that subclass with your table view.
I write something like this. It's simple but can elucidate solution
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseid", forIndexPath: indexPath)
// Configure the cell...
let contentView = cell.contentView
let subviews = contentView.subviews
for view in subviews {
if 100 == view.tag {
let label = view as! UILabel
label.text = self.datas[indexPath.row]
} else if 200 == view.tag {
view.layer.cornerRadius = 10.0
}
}
return cell
}
the key point is config every thing in tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
the view in code with tag 200 is a background view has same frame with label, I use layout constraint in storyboard to make sure its size and position.

set button on bottom of static tableView

hello I want to set the programmatically created button at bottom of static tableView. The problem I am having is the botton stays at the bottom on smaller phones(5s) which is completely fine. But on 6s Plus it shows white area underneath the button. Meaning the button is slightly above from the ground or above from edge of the bottom.
This is how I am setting the button
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
let footerView = UIView()
footerView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 50)
footerView.backgroundColor = UIColor.redColor()
let buttonNext = UIButton(type: UIButtonType.System)
buttonNext.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 50)
buttonNext.translatesAutoresizingMaskIntoConstraints = false
buttonNext.setTitle("NEXT", forState: UIControlState.Normal)
buttonNext.backgroundColor = UIColor.blueColor()
footerView.addSubview(buttonNext)
footerView.layoutIfNeeded()
return footerView
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 50
}
It sounds like you want a footer at the bottom of the table, rather than at the end of the section. In this case you should use the table's tableFooterView property. You could do this with the following code in viewDidLoad (or elsewhere):
let footerView = UIView()
footerView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 50)
footerView.backgroundColor = UIColor.redColor()
let buttonNext = UIButton(type: UIButtonType.System)
buttonNext.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 50)
buttonNext.translatesAutoresizingMaskIntoConstraints = false
buttonNext.setTitle("NEXT", forState: UIControlState.Normal)
buttonNext.backgroundColor = UIColor.blueColor()
tableView.tableFooterView = footerView

Resources