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
Related
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
I changed a tableView from Plain to Grouped so that the header/footer does not float over the table and stays anchored to the top and bottom of the table. That was straightforward, but now the font style formatting that I setup is not working. Strangely all other formatting of the header/footer seems to be working though. Any thoughts on what is going and what I am missing are appreciated!
Code below:
// Setup format of the header
func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let title = UILabel()
title.font = UIFont(name: "Avenir Book", size: 12)
title.textColor = UIColor.whiteColor()
let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
header.contentView.backgroundColor = UIColor(red: 30/255, green: 30/255, blue: 50/255, alpha: 1)
header.textLabel!.font = title.font
header.textLabel?.textColor = title.textColor
header.textLabel?.numberOfLines = 0
header.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
header.textLabel?.textAlignment = NSTextAlignment.Center
}
In a Plain table all of the above works great and looks like this:
However, when I change to Grouped table all of the formatting seems to show up except for the font style this this:
I am puzzled about where the ALL CAPS is coming from.
I tried to implement the solution from this question/answer, but could not get it to work either. Thanks for your ideas!
Assuming that you provided the string for the section title in this method:
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
But in a grouped tableview, that string is changed to all caps. To remove the all caps, try adding one of these two lines in the willDisplayHeaderView method:
header.textLabel?.text = header.textLabel!.text!.capitalizedString
header.textLabel?.text = header.textLabel!.text!.lowercaseString
The first one will capitalize the first letter of every word and the second will make everything lowercase. If you don't want either of those you could add the string directly:
header.textLabel?.text = "Here are a few options for you. Select to learn more"
Swift 4
use this delegates:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label: UILabel = {
let label = UILabel()
label.textAlignment = .right
label.textColor = .white
label.backgroundColor = .clear
label.font = UIFont.systemFont(ofSize: 20)
return label
}()
return label
}
and don't forget to set height for header:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70
}
hope this help.
Update for >=iOS 14, with the UIListContentConfiguration API:
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
guard let headerView = view as? UITableViewHeaderFooterView else {
return
}
var config = headerView.defaultContentConfiguration()
// This needs to explicitly be set if the table view is Grouped style
// (which it is, to hide sticky backgrounds on section headers),
// as the deafult style for
config.textProperties.transform = .none
// Other properties (color, font) as needed...
// Notably, `ContentConfiguration` requires setting content here as well,
// separate from `titleForHeaderInSection`
config.text = "MyText"
headerView.contentConfiguration = config
headerView.setNeedsUpdateConfiguration()
}
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
}
I have a tableview need to be updated very second. The code are as following. I design the headerview to have a dropdown function, when the header tap the rest are displayed. The code will crashes when I am trying to tap the header, the thread stops, xcode is not giving any hint on how and why.
func didListOfBLEDevicesUpdate(newDevice: BLEDevice)
{
println("receivedDevice from scanner every second: \(newDevice.deviceName)")
self.deviceTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return BLEDevice.listOfDevices.items.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1;
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return BLEDevice.listOfDevices.items[section].deviceName
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(IsExpandedMode[indexPath.section] == true){
return 400
}
return 70;
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))
headerView.backgroundColor = UIColor.grayColor()
headerView.tag = section
let headerString = UILabel(frame: CGRect(x: 10, y: 10, width: tableView.frame.size.width-10, height: 30)) as UILabel
headerString.text = BLEDevice.listOfDevices.items[section].deviceName
headerView .addSubview(headerString)
let headerTapped = UITapGestureRecognizer (target: self, action:"sectionHeaderTapped:")
headerView .addGestureRecognizer(headerTapped)
return headerView
}
func sectionHeaderTapped(recognizer: UITapGestureRecognizer) {
println("Tapping working")
println(recognizer.view?.tag)
var indexPath : NSIndexPath = NSIndexPath(forRow: 0, inSection:(recognizer.view?.tag as Int!)!)
if (indexPath.row == 0) {
var collapsed = self.IsExpandedMode [indexPath.section]
collapsed = !collapsed;
self.IsExpandedMode[indexPath.section] = collapsed
//reload specific section animated
var range = NSMakeRange(indexPath.section, 1)
var sectionToReload = NSIndexSet(indexesInRange: range)
self.deviceTableView.reloadSections(sectionToReload, withRowAnimation:UITableViewRowAnimation.Fade)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : DeviceTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! DeviceTableViewCell
let row = indexPath.row
cell.deviceName!.text = BLEDevice.listOfDevices.items[row].deviceName
cell.connectionStatus.text = BLEDevice.listOfDevices.items[row].connectionStatus
cell.deviceSignalStrengthen.text = BLEDevice.listOfDevices.items[row].RSSI
cell.manufacturerData.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataManufacturerData
cell.serviceUUID.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataServiceUUIDs
cell.serviceData.text = DataConvertHelper.getNSDictionary(BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataServiceData)
cell.TxPowerLevel.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataTxPowerLevel
cell.IsConnectable.text = DataConvertHelper.getBool(BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataIsConnectable)
cell.solicitedServiceUUID.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataSolicitedServiceUUIDs
cell.shortenedLocalName.text = BLEDevice.listOfDevices.items[row].advertisementPackage.cBAdvertisementDataLocalName
return cell
}
Use reload sections and reload rows rather than reloading data
The method you have used to handle the table seems to be rather complex. An alternative would be as follows:
1) Assumption from you code is that each device is associated with a section. As noted in the comments, your cellForRorAtIndexPath method seems to be using [row] to index your data model, but the model is based on [section] as you always return the number of rows as 1 for every section and the number of sections is the number of devices.
2) Rather than using a header view for each section and having to add gesture recognizers, simply create a custom cell to represent the device and make this row 0 of the section.
3) So each device is associated with a section, and row 0 of each section is the header information cell, NOT a header view. Make the header view nil. You can use a header height to leave a gap between sections.
4) Add code to detect selection of cells. When the cell row is 0, its the header cell. If the device is collapsed, set it to be expanded and vice versa and reload the section.
5) Make a new custom cell for you dropdown information. this will be row 1 of any section which is showing information.
6) Update your number of rows in section to return 2 if expanded, or 1 if collapsed.
7) Update cellForRowAtIndexPath to return the header cell for row 0 and the detail cell for row 1. Make sure to fix the [row] indexing to be [section] indexing.
This gives you a table of device header cells, which when clicked insert a detail cell below and when clicked again remove it and no gesture recognizers needed.
You need to make sure that your data model updates are working correctly. Seems from your errors that you are not updating the data model properly: in particular removal of devices.
I'm trying to write a check in App, where after the user enters information on the first view the view changes to a tableView of meetings. Currently I'm facing two issues; the first issue is the cell scroll past the header and under the status bar, How do I fix this? the second issue is scrolling down pulls the header with it, is there a way to change this?
I looked online a bit, and some people suggested using a Nav Controller and putting a UItable view inside of it. I'm trying to avoid the StoryBoard so I'm wondering how do I do this using code.
Here is my code so far
class MeetingsViewController: UITableViewController, UITableViewDelegate, {
#IBOutlet var meetingsView : UITableView
var meetings = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Meetings"
![enter image description here][1]tableView.registerClass(MeetingCell.self, forCellReuseIdentifier: "cell")
let edgeInsets = UIEdgeInsetsMake(20, 0, 0, 0)
self.tableView.contentInset = edgeInsets
self.tableView.scrollIndicatorInsets = edgeInsets
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(tableView: UITableView!, titleForHeaderInSection section: Int) -> String!
{
return "Meetings"
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as MeetingCell
if let path = indexPath {
//let entry = news[path.row] as NSDictionary
cell.text = "Meeting Name "
cell.detailTextLabel.text = "Time "
}
return cell
}
}
Try this in your controller's init():
let height = UIApplication.sharedApplication().statusBarFrame.size.height
let insets = UIEdgeInsets(top: height, left: 0, bottom: 0, right: 0)
self.tableView.contentInset = insets
self.tableView.scrollIndicatorInsets = insets