I'm trying to return two seperate .counts in my UITableView. I kind of know the logic behind it but not using the syntax right. How can I do this? What I want it to do is fill my tableview up with the both .counts. I would also like to put in a label on top of the first section and second section. Anything would help!
Here is what I have so far but I'm only getting the first section of cells. Why isnt it displaying both .counts?
func tableView(tableView: UITableView, numberOfSectionsInTableView: Int) -> Int{
return 2;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section == 0){
return featSubNames.count
}
else{
return subCatNames.count
}
}
func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
if (section == 0){
????
}
if (section == 1){
????
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
// Configure the cell
switch (indexPath.section) {
case 0:
cell.textLabel?.text = featSubName[indexPath.row]
cell.textLabel?.textColor = UIColor.whiteColor()
case 1:
cell.textLabel?.text = subCatNames[indexPath.row]
cell.textLabel?.textColor = UIColor.whiteColor()
default:
cell.textLabel?.text = "Other"
}
return cell
}
Your code seems to be right. To get the UIViews on the header of each section, you can return any object that inherits from a UIView(this doesn't mean that would be nice to). So, you can return a small container with a UIImageView and a UILabel, if you want this for example.
Your code for the viewHeader would be something like this:
func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
// instantiate the view to be returned and placed correctly
let commonLabel = UILabel(frame: CGRectMake(0, 0, tableWidth, 30))
commonLabel.textColor = .blackColor()
commonLabel.textAlignment = .Center
// ... other settings in the properties
if section == 0 {
commonLabel.text = "section1"
// other settings for the first section
}
if section == 1 {
commonLabel.text = "section2"
// other settings ...
}
return commonLabel
}
Notes:
Be sure to set the container view's frame,for sectionHeaders the top view in the hierarchy don't accept auto layout.
Implementing the viewForHeaderInSection method is more accessible to custom UI's, you can use the TitleForHeaderInSection if you wish, but it's limited for more complex stuffs.
EDIT:
If you're using storyboards, you still can use this code, although it's not so elegant in terms of apps that use IB for the UI. For this, you might take a look at this link: How to Implement Custom Table View Section Headers and Footers with Storyboard
. This implements only the view using storyboards, but the delegate part must to be wrote.
Related
I am trying to create a single table in Xcode and I want 3 sections in that table each with different headers.
I want to stick only the header of last section when it scrolls on top not the other sections.
Is there a way to do so please suggest me..
1- Yes, you can do that. In numberOfSections method of UITableViewDelegate, specify how many sections you want. And in viewForHeaderInSection method of UITableviewDelegate, provide a custom view for each section.
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// Make custom header view for each section
if section == 0 {
let header1 = UIView()
return header1
} else if section == 1 {
let header2 = UIView()
return header2
} else if section == 2 {
let header3 = UIView()
return header3
}
}
2- No, you can not do that. UITableView does not allow you to specify that which particular header should stick to the top and which one should not.
You can set the UITableView style to Plain. It will stick all the section header while scrolling.
I think that is not possible to stick the particular section header.
You could achieve the same behaviour by having a custom cell that would act as the header in the first two sections. And then remove the actual header for those.
For example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0 || indexPath.section == 1) && indexPath.row == 0 {
return HeaderCell()
}
// return your desired cells (make sure to handle the number of
// rows properly as there is effectively 1 more than normal in
// in the first two sections now)
}
You'll also need to adjust the headers for the first two sections:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 2 { return YourHeaderView() }
return nil
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 2 { return yourHeight }
return 0
}
Also, in the numberOfRows delegate function, you should make sure you add 1 (which will be the HeaderCell).
I have a table view in Swift with headers that expand or contract to show or hide cells, but for some reason there are extra row dividers in the middle of the page for no reason, and the label in the header is not showing up.
class ExpandableHeader: UITableViewHeaderFooterView {
var section: Int = 0
let expandLabel = UILabel()
}
func numberOfSections(in tableView: UITableView) -> Int {
// return number of section in table from data
return list.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return number of rows in each section from data
return list[section].items.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create cell with the identifier which was set in the storyboard prototype cell
// set cell data/name from our data object
if indexPath.row < list[indexPath.section].items.count {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = list[indexPath.section].items[indexPath.row].name
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "custom", for: indexPath)
return cell
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// create a header of type of our subclassed header with section number
let headerView = ExpandableHeader()
headerView.expandLabel.text = "+"
headerView.expandLabel.frame.size.height = 30
headerView.expandLabel.frame.size.width = 30
headerView.expandLabel.textAlignment = NSTextAlignment.center
headerView.addSubview(headerView.expandLabel)
headerView.expandLabel.frame.origin.x = view.frame.maxX - headerView.expandLabel.frame.width
// assign selected/current section number to the header object
headerView.section = section
// create Gesture Recognizer to add ability to select header to this cutsom header with an action
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(headerClicked(sender:)))
// add Gesture Recognizer to our header
headerView.addGestureRecognizer(tapGesture)
return headerView
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// check if row's section expanded parameter is set to true or false. set height of rows accordingly to hide or show them
if list[indexPath.section].expanded == true {
return 44
} else {
return 0
}
}
I added the relevant lines of code above. When you change the row height to 0, should the cell separator lines also be changed to height 0, or hidden?
Why is the header in each section not going all the way to the right side of the screen when the storyboard has it all the way to the right edge?
Is this also why the text label for the header is not being displayed? Because the right edge is getting cut off?
Sorry for the basic questions, I'm still getting the hang of this, so any suggestions would be much appreciated.
To hide extra row divider use below lines of code
let backgroundView = UIView(frame: CGRect.zero)
self.tableView.tableFooterView = backgroundView //instead of tableView give your tableView name
I am new to iOS Development and I just implemented a simple expandable sections UITableView. I am not able to understand why some rows disappear and sometimes change position when the row heights are recalculated on tapping the section header. I went through all the already answered questions on this topic and have not been able to find the right solution.
Following is a scenario:
Launch the app:
Tap on the section header:
Section expands
All other headers disappear
Tap again
Section collapses
The headers continue to be blank
Scrolled to the bottom and back to the top
The positions of headers changed
Scrolled to the bottom and back to the top again
The positions of headers changed again with some cells still blank
Things I have already tried:
Wrapping reloadRowsAtIndexPaths in updates block (beginUpdates() and endUpdates())
Using reloadRowsAtIndexPaths with animation set to .none
Removing reloadRowsAtIndexPaths at all while keeping the updates block
Using reloadData() instead which actually works but I lose animation
Code:
Here is the link to the project repository.
You're using cells for the header. You shouldn't do that, you need a regular UIView there, or at least a cell that's not being dequeued like that. There's a few warnings when you run it that give that away. Usually just make a standalone xib with the view and then have a static method like this in your header class. Make sure you tie your outlets to the view itself, and NOT the owner:
static func view() -> HeaderView {
return Bundle.main.loadNibNamed("HeaderView", owner: nil, options: nil)![0] as! HeaderView
}
You're reloading the cells in the section that grows, but when you change the section that's grown you'd need to at least reload the former section for it to take the changes to it's cell's height. You can reload the section by index instead of individual rows in both cases
Ok as you ask, I am changing my answer according to you.
import UIKit
class MyTableViewController: UITableViewController {
let rows = 2
var categories = [Int](repeating: 0, count: 10)
struct Constants {
static let noSelectedSection = -1
}
var selectedSection: Int = Constants.noSelectedSection
func selectedChanged(to selected: Int?) {
let oldIndex = selectedSection;
if let s = selected {
if selectedSection != s {
selectedSection = s
} else {
selectedSection = Constants.noSelectedSection
}
tableView.beginUpdates()
if(oldIndex != -1){
tableView.reloadSections([oldIndex,s], with: .automatic)
}else{
tableView.reloadSections([s], with: .automatic)
}
tableView.endUpdates()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("reloading section \(section)")
return (selectedSection == section) ? rows : 0;//rows
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return tableView.rowHeight
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.rowHeight
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "Header")
if let categoryCell = cell as? MyTableViewCell {
categoryCell.category = section + 1
let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
recognizer.numberOfTapsRequired = 1
recognizer.numberOfTouchesRequired = 1
categoryCell.contentView.tag = section;
categoryCell.contentView.addGestureRecognizer(recognizer)
}
return cell?.contentView
}
func handleTapGesture(recognizer: UITapGestureRecognizer) {
if let sindex = recognizer.view?.tag {
selectedChanged(to: sindex)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Body", for: indexPath)
if let label = cell.viewWithTag(1) as? UILabel {
label.text = "Body \(indexPath.section + 1) - \(indexPath.row + 1)"
}
return cell
}
}
As you can see now I am just reloading a particular section instead of reloading the whole table.
also, I have removed gesture recognizer from the cell & put this into the main controller.
I am trying to make a table view with multiple sections, but for some (presumably dumb) reason only the first section is visible on my table view.
var labels = ["label1", "label2"]
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
var sectionHeader = ""
if section == 0 {
sectionHeader = "first section header"
} else if section == 1 {
sectionHeader = "second section header"
}
return sectionHeader
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
if indexPath.section == 0 {
cell.textLabel!.text = labels[0]
} else if indexPath.section == 1 {
cell.textLabel!.text = labels[1]
}
return cell
}
The header for the first section appears along with a cell with label text "label1" but the second section is no where to be found.
Is there some sort of setting I am not setting? What am I doing wrong here.
Your code looks good , Few things you can check
1> Tableview's frame is not large enough to fit 2 section and it's not scroll-able also.
2> heightForRow delegate method or viewForHeader or heightforHeader returns 0 or nil
3> Your constraints are breaking at runtime and that's why tableview is not visible properly (you need to check console at runtime)
You can start visual debugger of XCode at runtime and see if your table view is not hidden by some other component etc... (Debug View Hierarchy)
I have a tableView set up so that when a cell is touched, it expands in height to reveal more information. The tableView has 5 sections.
I have a bug: when a cell expands, all headersCells below that cell go invisible. The console outputs the following: "[31233:564745] no index path for table cell being reused"
In my storyboard I have 2 custom cells : "myCell" for the data bearing cells, and "headerCell" for the headers.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let thisGoal : GoalModel = goalArray[indexPath.section][indexPath.row]
if self.currentPath != nil && self.currentPath == indexPath {
self.currentPath = nil
} else {
self.currentPath = indexPath
}
tableView.beginUpdates()
tableView.endUpdates()
}
If I enter tableView.reloadData() in between the begin/end updates, it functions properly, although the header background turns black, and loses animation.
I have all of the stuff for headers declared in: func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
what am I missing? I'd really like the tableView to still have animations, and keep the background clearColor().
Thanks in advance. I did read through the objective C answers, but couldn't get them to work for me. I'd really appreciate some SWIFT help.
I think the problem is the no index path for table cell being reused.
I found an answer in the console output. Use this code in the header function:
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
Do not return your headerCell, or your reusable identifier. Return the reuseIdentifier.contentView. For me it's: return headerCell!.contentView.
Just to add, I was baffled for WAY longer than I should have been as to why I couldn't refer to the contentView of my cell, when I could quite clearly see it was there. My custom class (using UITableViewCell rather than UITableViewHeaderFooterView) would return a fatal error each time. Therefore make sure any custom styling is setup under UITableViewHeaderFooterView class like:
class CustomHeaderCell: UITableViewHeaderFooterView {
You will also need to register the resuableIdentifer like this:
tableView.registerNib(UINib(nibName: "HeaderCell", bundle: nil), forHeaderFooterViewReuseIdentifier: "CellHeader")
Then this bad boy:
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableHeaderFooterViewWithIdentifier("CellHeader") as! CustomHeaderCell!
return headerCell!.contentView
}
Since I'm not at 50 reputation yet, I can't comment on the previous answer, so I apologize for listing this as another answer.
Returning the ContentView will make the function work but will remove all formatting done to the reuseIdentifier (headerCell)
headerCell.backgroundColor = UIColor.cyanColor()
This will NOT provide a Cyan color to your headerCell
To fix this, just add the ".contentView" to your formatting lines
headerCell.contentView.backgroundColor = UIColor.cyanColor()
Table view headers in 2 tables disappeared when I converted my app to IOS 10 - I found the reason in Apple developer API documentation on table headers. When I added the following, the missing headers reappeared!
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
return 44 // where 44 is the header cell view height in my storyboard
}
You could wrap the TableViewCell inside an UIView
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 50)) // 50 = Header height
guard let headerCell = tableView.dequeueReusableCell(withIdentifier: "MyHeaderView") as? MyHeaderView else { fatalError(" Failed to load MyHeaderView") }
headerCell.frame = containerView.bounds
containerView.addSubview(headerCell)
return containerView
}
I had the same bug because I was returning a cell using dequeue method instead of a UITableViewHeaderFooterView.
Solution:
Add a view outside of the view hierarchy
Set the type to UITableViewHeaderFooterView
Customize
Link to an #IBOutlet
In func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? return the outlet
Common pitfalls:
Don't forget to set the header sizes
Don't forget to set the outlet as strong.