Change headers in UITableView static cells [duplicate] - ios

This question already has answers here:
How to customise header section in static cell?
(3 answers)
Closed 6 years ago.
I have this UITableView with static cells:
I'd like to change the header field and center horizontally in the cell. How can I do using Swift?
Thanks in advance.

first of all, of course you can do this using swift, and now this is the code that you need to put in your viewController to make this work
you need to put your viewController as UITableViewDelegate and then implement this methods
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return X; //X is the value of height of your header
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: X)) //X is the value of height of your header
let label = UILabel(frame: headerView.frame)
label.text = "TESTING"
label.textAlignment = NSTextAlignment.Center
headerView.addSubview(label)
return headerView;
}
I hope this help you

Sounds like you are wanting to use:
tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
}
Using this you could code the UI you are looking for, create the label and set the frame to what you need and set the text to what you would want it to be. Inside would either if check or use a switch statement for section, and set the text accordingly, then simply return the label or view, however you decide to implement your solution. I have done something like this in a tableView footer, I'll post the code and link to the documentation for reference:
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView.init(frame: CGRectMake(0, 0, tableView.frame.size.width, 50))
//label
let label : UILabel = UILabel.init(frame: CGRectMake(30, 0, 150, 50))
label.text = "Calorie Total :"
//label to hold calorie total for the section
let totalLabel = UILabel.init(frame: CGRectMake(185, 0, 50, 50))
var total = Int()
//grab the total for the section
switch section {
case 0:
total = calculateCalories(breakfastFoodArray)
break
case 1:
total = calculateCalories(lunchFoodArray)
break
case 2:
total = calculateCalories(dinnerFoodArray)
break
case 3:
total = calculateCalories(snackFoodArray)
break
default:
break
}
totalLabel.text = String(total)
footerView.addSubview(label)
footerView.addSubview(totalLabel)
let footerExentsionView = UIView.init(frame: CGRectMake(0, 50, tableView.frame.size.width, 10))
footerExentsionView.backgroundColor = UIColor.whiteColor()
footerView.addSubview(footerExentsionView)
return footerView
}

Related

Message Bubble Cut Off in TableView Cell

I am creating a chat feature in my app, but the cells are getting cut off. I've reviewed the Ray Wenderlich tutorial and the SO questions, but I think my problem is that I combine code with auto layout and I'm missing something (for instance, I don't put a label for the message text on the storyboard, but I do put the imageView). Here's what it looks like when I run it:
Here is the code for the tableView setup:
In viewDidLoad:
tableView.estimatedRowHeight = 600
tableView.rowHeight = UITableView.automaticDimension
Other code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get a preconfigured messageCell
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.Storyboard.messageCell) as! MessageCell
// Send the message to it for set up.
cell.setMessage(message: messages[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
And here is the setMessage function in the custom cell:
#IBOutlet var messageImage: UIImageView!
func setMessage(message: Message) {
if message.who == "me" {
// User wrote this message, show it on the right in blue as an outgoing message.
let text = message.txt
let label = UILabel()
label.numberOfLines = 0
label.font = UIFont.systemFont(ofSize: 18)
label.textColor = .white
label.text = text
let constrainRect = CGSize(width: 0.66 * self.frame.width,
height: .greatestFiniteMagnitude)
let boundingBox = text.boundingRect(with: constrainRect, options: .usesLineFragmentOrigin, attributes: [.font: label.font!], context: nil)
label.frame.size = CGSize(width: ceil(boundingBox.width), height: ceil(boundingBox.height))
let bubbleImageSize = CGSize(width: label.frame.width + 28, height: label.frame.height + 20)
messageImage = UIImageView(frame: CGRect(
x: self.frame.width - bubbleImageSize.width - 20,
y: self.frame.height - (bubbleImageSize.height),
width: bubbleImageSize.width,
height: bubbleImageSize.height))
let bubbleImage = UIImage(named: "outgoing-message-bubble")?
.resizableImage(withCapInsets: UIEdgeInsets(top: 17, left: 21, bottom: 17, right: 21), resizingMode: .stretch)
.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
messageImage.image = bubbleImage
self.addSubview(messageImage)
label.center = messageImage.center
self.addSubview(label)
} else {
// Other person wrote this message. Show it on the left in gray.
}
}
There are no constraints on messageImage, but I have tried it with constraints of zero around it and the result is the same. I've run the code with breaks and confirmed the sizes are all accurate for the text and the bubble image. But the content view of the cell is cutting off the height to a standard cell size. I've worked on this for over a day. Any help would be appreciated.

Dynamically change UITableViewCell height - Swift 4

How do I go about dynamically changing the UITableViewCell height? I've tried implementing the following, but for some reason, it isn't working. The code crashes as soon as I load the view controller displaying this table view
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cell = tableView.cellForRow(at: indexPath) as! AvailableRideCell
return cell.getHeight()
}
This is the getHeight function in my AvailableRideCell
func getHeight() -> CGFloat {
return self.destinationLabel.optimalHeight + 8 + self.originLabel.optimalHeight + 8 + self.priceLabel.optimalHeight
}
And this is the optimalHeight function
extension UILabel {
var optimalHeight : CGFloat {
get {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = self.font
label.text = self.text
label.sizeToFit()
return label.frame.height
}
}
}
Keep in mind that UITableViewCell is reused. So getting the height of the current cell can be unstable.
A better way is to have one fake/placeholder cell (I call the calculator cell) and use that to calculate the size of the cell.
So in the heightForRowAt method, you get the data instead of the cell.
Put that data inside the calculator cell and get the height from there.
You code crashes because of this line
let cell = tableView.cellForRow(at: indexPath) as! AvailableRideCell
From Apple Documentation we know that this method is used for optimization. The whole idea is to get cells heights without wasting time to create the cells itself. So this method called before initializing any cell, and tableView.cellForRow(at: indexPath) returns nil. Because there are no any cells yet. But you're making force unwrapping with as! AvailableRideCell and your code crashed.
So at first, you need to understand, why you should not use any cells inside the cellForRow(at ) method.
After that, you need to change the logic so you could compute content height without calling a cell.
For example, in my projects, I've used this solution
String implementation
extension String {
func height(for width: CGFloat, font: UIFont) -> CGFloat {
let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
let actualSize = self.boundingRect(with: maxSize,
options: [.usesLineFragmentOrigin],
attributes: [NSAttributedStringKey.font: font],
context: nil)
return actualSize.height
}
}
UILabel implementation
extension String {
func height(for width: CGFloat, font: UIFont) -> CGFloat {
let labelFrame = CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)
let label = UILabel(frame: labelFrame)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.font = font
label.text = self
label.sizeToFit()
return label.frame.height
}
}
With that, all you need to do is to compute your label and store its font.
var titles = ["dog", "cat", "cow"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// number of rows is equal to the count of elements in array
return titles.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cellTitle = titles[indexPath.row]
return cellTitle.height(forWidth: labelWidth, font: labelFont)
}
Dynamic rows height changing
If you'll need to update row height, all you need to do is to call this method when your content had been changed. indexPath is the index path of changed item.
tableView.reloadRows(at: [indexPath], with: .automatic)
Hope it helps you.
You don't mention if you are using Auto Layout, but if you are, you can let Auto Layout manage the height of each row. You don't need to implement heightForRow, instead set:
tableView.rowHeight = UITableViewAutomaticDimension
And configure your UILabel with constraints that pin it to the cell's content view:
let margins = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
cellLabel.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
cellLabel.trailingAnchor.constraint(equalTo: margins.trailingAnchor),
cellLabel.topAnchor.constraint(equalTo: margins.topAnchor),
cellLabel.bottomAnchor.constraint(equalTo: margins.bottomAnchor)
])
Each row will expand or contract to fit the label's intrinsic content size. The height is automatically adjusted if the device landscape/portrait orientation changes, without re-loading the cell. If you want the row height to change automatically when the device's UIContentSizeCategory changes, set the following:
cellLabel.adjustsFontForContentSizeCategory = true

UITableViewCell separators based on array

I have a JSON array called contacts containing around 100 contacts.
Now I want to copy the functionality of the contacts.app by showing a letter section title for each letter in the alphabet on the right.
I've tried several things but I'm wondering what would be the most memory efficient way of doing this?
You can use this two methods
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionHeader = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 40))
let lblTitle:UILabel = UILabel(frame: CGRect(x: 20, y: 10, width: tableView.frame.size.width-20, height: 21))
var strTitle : String!
switch section {
case 0:
strTitle = "A"
break
case 1:
strTitle = "B"
break
case 2:
strTitle = "C"
break
default:
break
}
lblTitle.text = strTitle
sectionHeader.addSubview(lblTitle)
return sectionHeader
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
Here I set Alphabets in switch case statement for Understanding purpose. You can Fetch this alphabet from Dictionary or Array Or Json Response according to your requirement.
It is general or Efficient solution. I hope it will help you.

Swift: Changing TableView Header text color - Crashing

Using Swift 3, I'm trying to change the Section's Header color programmatically.
How do I change the backgroundColor to white and Text Color to black?
The sections header changes color but no text appears and then when I add code to change the header text color it crashes
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'Can't add self as subview'
Swift Code
// Title for Header
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// Section Names
let sectionNames = ["Followed Blogs", "All Blogs"]
return sectionNames[section]
}
// View for Header
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
let headerLabel = UILabel()
let sectionNames = ["Followed Blogs", "All Blogs"]
headerLabel.text = sectionNames[section]
headerLabel.frame = CGRect(x: 45, y: 5, width: 100, height: 35)
headerLabel.addSubview(headerLabel)
if (section == 0) {
headerView.backgroundColor = UIColor.green
headerLabel.textColor = UIColor.black
} else {
if darkMode {
headerView.backgroundColor = UIColor.white
headerLabel.textColor = UIColor.black
} else {
headerView.backgroundColor = UIColor.black
headerLabel.textColor = UIColor.white
}
}
return headerView
}
// Height for Section
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 45
}
headerLabel.addSubview(headerLabel) is adding the label to self, which is the source of your error
Based on my understanding of your code, you should probably be using headerView.addSubview(headerLabel) instead
The text "Followed Blogs" doesn't fit it shows as "Followed B..."
This is (most likely) a layout issue, I'd personally investigate adding auto layout constraints to the label so that it binds to the top/bottom/left/right margins of the parent view
This is just to add on MadProgrammer's answer. I think instead of UIView you should use UITableViewHeaderFooterView
usage:
tableViewInstance.register(UITableViewHeaderFooterView.self, forHeaderFooterViewResuseIdentifier: "headerIdentifier")
Then in viewForHeaderInSection:
let tableViewHeader = tableview.dequeueReusableHeaderFooterView(withIdentifier: "headerIdentifier")
btw, regarding the text "Followed Blogs" not fitting in its because of your label's width is too small for the current font. Why not add a constraints like this:
headerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-5-[label]-5-|",
options: [],
metrics: nil,
views: ["tableView": headerLabel]))
headerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-5-[label]-5-|",
options: [],
metrics: nil,
views: ["tableView": headerLabel]))
You make your tableView's headerHeight be dynamic

UITableViewCell won't not receiving touches

I am implementing a view that displays a lot of information. The view is scrollable and inside of the view I implemented a non-scrollable table view holding user comments. I have all the auto-layout constraints and it appears to layout correctly however touches are not received below a certain row. It appears that the table view or something is blocking the views below from receiving the events but I am unable to trace down the issue.
I want the main scroll view's content size to grow as the comment table view grows. Keeping the post comment view at the bottom of the table view. Right now I can't select the last cell or the text field.
Comment Cell View
Simulator screenshot
Here is the code from the table view implementation:
commentsTableView.delegate = self
commentsTableView.dataSource = self
commentsTableView.estimatedRowHeight = 82
commentsTableView.rowHeight = UITableViewAutomaticDimension
commentsTableView.sectionHeaderHeight = UITableViewAutomaticDimension
commentsTableView.estimatedSectionHeaderHeight = 54
commentsTableView.estimatedSectionFooterHeight = 0
commentsTableView.sectionHeaderHeight = UITableViewAutomaticDimension
commentsTableView.tableFooterView = UIView(frame: CGRectZero)
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Comments"
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section != 0 { return nil }
let sectionTitle: String = self.tableView(tableView, titleForHeaderInSection: section)!
if sectionTitle == "" {
return nil
}
let view = UIView(frame: CGRectMake(0, 0, tableView.frame.width, 54))
let title = UILabel(frame: CGRectMake(60, 22, tableView.frame.width, 17))
view.addSubview(title)
title.text = sectionTitle
title.textColor = UIColor(red: (74 / 255), green: (74 / 255), blue: (74 / 255), alpha: 1.0)
title.backgroundColor = UIColor.clearColor()
view.backgroundColor = UIColor.clearColor()
title.font = UIFont(name: "ProximaNova-Semibold", size: 16.0)
view.layer.addBorder(.Bottom, color: UIColor.lightGrayColor().colorWithAlphaComponent(0.75), thickness: 0.5)
title.setNeedsDisplay()
view.setNeedsDisplay()
return view
}
You have to set the userInteractionEnabled to true to fire those events.
view.userInteractionEnabled = true
The Comment TextField is the last row of your comment TableView. So put comment TextField code in footer of the TableView as follows:
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let commentTextFieldView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 50))
// Your Comment TextField code
return commentTextFieldView
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 50.0
}
There is an another way to assign Comment TextField view as tableView footer:
myTableView.tableFooterView = commentTextFieldView

Resources