I have a grouped table view which I want to have customized section headers. But I'm having trouble showing the first section header for the table view.
I have two sections and the section header for the first section is not showing but the second one is:
I have seen similar problems and those problems where solved by implementing the heightForHeaderInSection but I have implemented that.
I'm setting the sections like this:
let kSectionHeaderContact: String = "CONTACT INFORMATION"
let kSectionHeaderFeedback: String = "FEEDBACK"
enum Sections: Int {
case ContactSection = 0
case FeedbackSection = 1
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == Sections.ContactSection.rawValue {
return contactObjectsDictionary.count
} else if section == Sections.FeedbackSection.rawValue {
return feedbackObjectsDictionary.count
} else {
return 0
}
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == Sections.ContactSection.rawValue {
sectionHeaderView.sectionHeaderTitle.text = kSectionHeaderContact
} else if section == Sections.FeedbackSection.rawValue {
sectionHeaderView.sectionHeaderTitle.text = kSectionHeaderFeedback
}
return sectionHeaderView
}
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return kContactTableViewSectionHeaderViewHeight
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifierContactTableViewCell, forIndexPath: indexPath) as! ContactTableViewCell
// Configure the cell...
var titleText: String = ""
var detailText: String = ""
if indexPath.section == Sections.ContactSection.rawValue {
let contactObject = contactObjectsDictionary[indexPath.row]
titleText = contactObject[kDictionaryTitleKey]!
detailText = contactObject[kDictionaryDetailKey]!
} else if indexPath.section == Sections.FeedbackSection.rawValue {
let feedbackObject = feedbackObjectsDictionary[indexPath.row]
titleText = feedbackObject[kDictionaryTitleKey]!
detailText = feedbackObject[kDictionaryDetailKey]!
}
cell.configureCell(titleText, detail: detailText)
return cell
}
I'm implementing my custom section header in the storyboard and then referencing it by an outlet:
Edited:
I want to be able to design my custom section header in the storyboard and not programmatically.
Problem is inside viewForHeaderInSection method.
You need to create new instance of UIView & need to return it.
OR
You can create Class for UITableViewHeaderHeaderView and Reuse it.
Feel free to ask if you have any doubts regarding this.
let titleFirstLabel: UILabel = {
let label = UILabel()
label.text = "Text"
label.frame = CGRect(x: 15, y: 10, width: 100, height: 20)
return label
}()
and viewDidLoad() add this code
tableView.addSubview(titleFirstLabel)
That work for me. Good luck :)
Related
I have a collapsible header for uitableview sections based on another stack overflow post (no idea where now, as that was months ago). As it happens, the testers found a weird bug where collapsing all of the sections pulls the table header view down.
*edit the table view header is just a UI view I dropped into the storyboard, inside the tableview, above the prototype cell. No significant constraints. Just height for the cells and the header. The tableview is pinned to the safe area.
Everything looks fine until you expand one of the sections off screen, then scroll it up so the rows start to slide under the floating section header at the top. Then you tap to collapse it. It collapses, but the header view is pulled down. It looks like it happens when the sections fit on one screen, and the rows were scrolled slightly before the collapse.
Any help would be appreciated.
In my demo project (happy to share), when the four sections are collapsed, it looks like this:
When the user expands some of the sections, scrolls so a section header is sticky at the top and the contents are scrolled under it, then collapses the sticky section header, it can look like this:
I have a protocol for the delegate:
protocol CollapsibleHeaderViewDelegate: class {
func toggleSection(header: CollapsibleSectionHeader, section: Int)
}
protocol SectionHeaderCollapsible {
var isCollapsed: Bool { get }
var rowCount: Int { get }
}
And the subclass of UITableVieHeaderFooterView:
class CollapsibleHeader: UITableViewHeaderFooterView {
#IBOutlet var sectionHeaderLabel: UILabel!
var collapsed = false
weak var delegate: CollapsibleHeaderViewDelegate?
var sectionItem: SectionHeaderCollapsible?
static let reuseIdentifer = "CollapsibleHeader"
func configure(headerText: String) {
textLabel?.text = headerText
}
override func awakeFromNib() {
super.awakeFromNib()
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapHeader)))
}
#objc private func didTapHeader(gestureRecognizer: UITapGestureRecognizer) {
guard let header = gestureRecognizer.view as? CollapsibleHeader else { return }
delegate?.toggleSection(header: self, section: header.tag)
}
}
Then the delegate does something like. this:
struct CollapsibleSection: SectionHeaderCollapsible {
var isCollapsed: Bool = false
var rowCount: Int {
get {
return isCollapsed ? 0 : dataContents.count
}
}
var dataContents: [String]
}
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
#IBOutlet var headerView: UITableView!
var sections = [CollapsibleSection(isCollapsed: false, dataContents: ["first", "second"]),
CollapsibleSection(isCollapsed: false, dataContents: ["red", "blue"]),
CollapsibleSection(isCollapsed: false, dataContents: ["seven", "five"]),
CollapsibleSection(isCollapsed: false, dataContents: ["Josephine", "Edward"])]
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let nib = UINib(nibName: "CollapsibleHeader", bundle: nil)
tableView.register(nib, forHeaderFooterViewReuseIdentifier: "CollapsibleHeader")
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].rowCount
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") else { fatalError() }
cell.textLabel?.text = sections[indexPath.section].dataContents[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let header = self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "CollapsibleHeader") as? CollapsibleHeader else { fatalError() }
header.sectionHeaderLabel.text = "Section \(section + 1)"
header.delegate = self
header.tag = section
return header
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 100
}
}
extension ViewController: CollapsibleHeaderViewDelegate {
func toggleSection(header: CollapsibleHeader, section: Int) {
sections[section].isCollapsed = !sections[section].isCollapsed
tableView.reloadSections([section], with: .fade)
}
}
EDIT:
Looks like my coworkers created a work around based on (or at least similar to) your answer:
if tableView.contentOffset.y < 0 {
var offset = tableView.contentOffset
offset.y = tableView.contentSize.height - tableView.bounds.height
tableView.setContentOffset(offset, animated: true)
} else {
tableView.setContentOffset(tableView.contentOffset, animated: true)
}
Faced same problem, apparently right after "reloadSections", tableView's contentOffset.y has some strange value (you can see it when print "tableView.contentOffset.y" before and after "reloadSections"). So I just set contentOffset after it uncollapse to 0 offset value:
let offset = tableView.contentOffset.y
// Reload section
tableView.reloadSections(IndexSet(integer: section), with: .automatic)
if !sections[section].isCollapsed {
tableView.contentOffset.y = offset - offset
}
Problem Statement:
I want to display text separated by "," on new custom UITableViewCell.
Problem: It displays all data in single custom cell only with multi-line property, as shown in below.
I want to display tableView like this way.
Now I'm trying to display above data separated by "," on each new custom cell, as shown in above screenshot, but it displays only 1st data and skip remaining data, as per my code.
let Meaning :String = "Aback,Abacus,Abandon,Able,Aboard"
let Smeaning :String = "Fabric,Habit,keen,Pace"
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
let fmeaning = Mmeaning.characters.split{$0 == ","}.map(String.init)
let smeaning = Hmeaning.characters.split{$0 == ","}.map(String.init)
for var i = 0; i < fmeaning.count; i += 1{
print(fmeaning[i])
//Here it prints all values perfectly
}
for var i = 0; i < smeaning.count; i += 1{
print(smeaning[i])
//Here it prints all values perfectly
}
Problem occurs here below two statements: display only 1st value in UITableView
cell.lblMeaning1.text = fmeaning[indexPath.row]
cell.lblMeaning2.text = smeaning[indexPath.row]
return cell;
}
How should I assign an Array to these two custom cells, so that it will display data on separate new custom cell?
For that you need to use section table with your tableView, also you need to make this calculation in viewDidLoad and then reload the tableView, For that declare your two instance of array like this in your viewController.
var languageDic = [String: [String]]()
var allLanguage = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let mMeaningArray = Mmeaning.characters.split{$0 == ","}.map(String.init)
let hMeaningArray = Hmeaning.characters.split{$0 == ","}.map(String.init)
self.languageDic = ["Marathi Meaning": mMeaningArray,"Hindi Meaning": hMeaningArray] // You can add other langaue in the dic sam way I have added this two
self.allLanguage = Array(self.languageDic.keys) as [String]
self.tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.allLanguage.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
return self.allLanguage[indexPath.section]
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.languageDic[self.allLanguage[section]].count
}
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
let str = self.languageDic[self.allLanguage[indexPath.section]][indexPath.row]
cell.lblMeaning1.text = str
return cell
}
Note: In cellForRowAtIndexPath you need only one label.
Edit: As of you want return the count of array that have more element you can try like this. F
var mMeaningArray = [String]()
var hMeaningArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.mMeaningArray = Mmeaning.characters.split{$0 == ","}.map(String.init)
self.hMeaningArray = Hmeaning.characters.split{$0 == ","}.map(String.init)
self.tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.mMeaningArray.count > self.hMeaningArray.count)? self.mMeaningArray.count : self.hMeaningArray.count
}
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
if indexPath.row < self.mMeaningArray.count {
cell.lblMeaning1.text = self.mMeaningArray[indexPath.row]
}
else {
cell.lblMeaning1.text = ""
}
if indexPath.row < self.hMeaningArray.count {
cell.lblMeaning2.text = self.hMeaningArray[indexPath.row]
}
else {
cell.lblMeaning2.text = ""
}
return cell
}
the numberOfRowsInSection() function should return fmeaning.count and smeaning.count... put an if to determine which table you want to have the correct row number.
also you cand display the fmeaning as a section in your tableview, and the smeaning as cells.
I have a Table view with data compiled from a Dictionary array where the keys are the section headers:
var data: Dictionary<String,[String]> = [
"Breakfast": ["Oatmeal","Orange Juice"],
"lunch": ["Steak","Mashed Potatoes","Mixed Veg"],
"Dinner": ["Chicken","Rice"],
"Snack": ["Nuts","Apple"]
]
var breakfastCalories = [100,200,300]
var lunchCalories = [300,400,500]
var DinnerCalories = [600,700,800]
var breakfast = 0
Below is the code for populating the Table View
override func viewDidLoad() {
super.viewDidLoad()
for value in breakfastCalories as NSArray as! [Int]{
breakfast = breakfast + value
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return data.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
let sectionString = Array(data.keys)[section]
return data[sectionString]!.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let sectionString = Array(data.keys)[section]
return sectionString
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
let sectionString = Array(data.keys)[indexPath.section]
cell.caloriesLabel.tag = indexPath.row
cell.caloriesLabel.text = String(breakfastCalories[indexPath.row])
cell.foodLabel.tag = indexPath.row
cell.foodLabel.text = data[sectionString]![indexPath.row]
return cell
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))
// self.myTableView.tableFooterView = footerView;
let label = UILabel(frame: CGRectMake(footerView.frame.origin.x - 15, footerView.frame.origin.y, footerView.frame.size.width, 20))
label.textAlignment = NSTextAlignment.Right
label.text = "Total Calories: \(breakfast) "
footerView.addSubview(label)
return footerView
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 20.0
}
My question is, how can I add the calories array for each section? so for breakfast, it will contain the calories from the breakfastCalories array, lunch section for the lunchCalories array etc.
I may be overthinking this but I can't get my head around this problem
Thanks
On the right the values are grabbed from the breakfastCalories however as mentioned each section contain the calories from the breakfastCalories array, lunch section for the lunchCalories array etc.
You could structure your calories similar to your data property, with similar keys:
var calories: Dictionary<String,[Int]> = [
"Breakfast": [100,200,300],
"lunch": [300,400,500],
"Dinner": [600,700,800]
]
That way you can pull out the right calories depending on what section you are showing and add them up to create a total for you label to show: (Add this where you create your footerView and just above where you set your label.text)
let dataKeysArray = Array(data.keys)[section]
let sectionString = dataKeysArray[section]
let mealCalories = calories[sectionString]
var totalCalories: Int = 0
for calories in mealCalories {
totalCalories += calories
}
label.text = "Total Calories: \(totalCalories) "
So, I have a few Swipe actions like delete, block, etc in my UITableView. I wanted to add headers to separate my two sections. So, I added a prototype cell, named it HeaderCell and then went to the view. I added one label, named headerLabe. My problem is that when I swipe for the actions, the header cells were moving as well, which looked bad. I researched, and found a solution to just return the contentView of the cell. However, when I do this, the label has not shown up. I have tried a dozen different solutions, and nothing has worked, so I have turned to SO. Can anyone help me?
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell : CustomHeaderTableViewCell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! CustomHeaderTableViewCell
if section == 0 {
headerCell.headerLabel.text = "Thank You's"
} else if section == 1 {
headerCell.headerLabel.text = "Conversations"
}
return headerCell.contentView
}
Thanks so much.
You can use a section Header as #ozgur suggest.If you still want to use a cell.
Refer to this datasource method
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if indexPath = YourHeaderCellIndexPath{
return false
}
return true
}
check the following methods
In your UIViewController use the following
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCellWithIdentifier("HeaderCell") as! WishListHeaderCell
headerCell.lblTitle.text = cartsData.stores_Brand_Name
let imgVw = UIImageView()
imgVw.frame = CGRectMake(8, 18, 25, 25)
imgVw.image = UIImage(named: "location.png")
let title = UILabel()
title.frame = CGRectMake(41, 10, headerCell.viwContent.frame.width - 49, 41)
title.text = cartsData.stores_Brand_Name
title.textColor = UIColor.whiteColor()
headerCell.viwContent.addSubview(imgVw)
headerCell.viwContent.addSubview(title)
return headerCell.viwContent
}
In your UITableViewCell use the following
import UIKit
class HeaderCell: UITableViewCell {
#IBOutlet weak var viwContent: UIView!
#IBOutlet weak var imgIcn: UIImageView!
#IBOutlet weak var lblTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.viwContent.backgroundColor = UIColor.grayColor()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//UITableViewCell
let headerCell = tableView.dequeueReusableCellWithIdentifier("headerCell") as! SecJobCCHeaderTableViewCell
// Cell Rect
var cellRect : CGRect = headerCell.frame
cellRect.size.width = screenBounds.width
// Header Footer View
let headerFooterView = UITableViewHeaderFooterView(frame : cellRect)
//Adding Gesture
let swipeGestRight = UISwipeGestureRecognizer(target: self, action:#selector(AddSecJobCostCentreViewController.draggedViewRight(_:)))
swipeGestRight.enabled = true
swipeGestRight.direction = UISwipeGestureRecognizerDirection.Right
headerFooterView.addGestureRecognizer(swipeGestRight)
// Update Cell Rect
headerCell.frame = cellRect
// Add Cell As Subview
headerCell.tag = 1000
headerFooterView.addSubview(headerCell)
// Return Header Footer View
return headerFooterView
}
func draggedViewRight(sender:UISwipeGestureRecognizer) {
// Swipe Gesture Action
let currentHeaderView = sender.view?.viewWithTag(1000) as! SecJobCCHeaderTableViewCell
}
I'm using XCode 6.3 to build a TableView of the different Fonts in iOS 8. First, per the book I'm reading, it said that nothing needed to be done regarding the height of the table rows, given that iOS8 takes care of that for you, so once I had everything per the book, the rows should update their heights based on their content, which wasn't the case. Then I tried to play with tableView.rowHeight and I set it equal to UITableViewAutomaticDimension in the TableViewController's viewDidLoad function, and that didn't work either. I also tried changing the height of the rows from Interface Builder, and that didn't seem to have any effect on the heights either. My code is as follows:
class RootViewController: UITableViewController {
private var familyNames: [String]!
private var cellPointSize: CGFloat!
private var favoritesList: FavoritesList!
private let familyCell = "FamilyName"
private let favoritesCell = "Favorites"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
familyNames = sorted(UIFont.familyNames() as! [String])
let preferredTableViewFont = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
favoritesList = FavoritesList.sharedFavoritesList
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
func fontForDisplay(atIndexPath indexPath: NSIndexPath) -> UIFont? {
if indexPath.section == 0 {
let familyName = familyNames[indexPath.row]
let fontName = UIFont.fontNamesForFamilyName(familyName).first as! String
return UIFont(name: fontName, size: cellPointSize)
} else {
return nil
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return favoritesList.favorites.isEmpty ? 1 : 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return section == 0 ? familyNames.count : 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "All Font Families" : "Favorite Fonts"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier(familyCell, forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel!.text = familyNames[indexPath.row]
cell.detailTextLabel!.text = familyNames[indexPath.row]
return cell
} else {
return tableView.dequeueReusableCellWithIdentifier(favoritesCell, forIndexPath: indexPath) as! UITableViewCell
}
}
}
When I run this in the simulator, everything looks right until I scroll all the way to the bottom and I get this:
The attributes of the FontFamily cell are: style = subtitle, and accessory = disclosure indicator.
Any ideas on what I'm be doing wrong?
You must set self.tableView.estimatedRowHeight