Get section header cell in gesture method - ios

I am working on a 'UITableView' with different section headers. Section header contains a tab gesture recognization to expand and collapse the section.
In the section header view, I have used an image for the accessory icon to show the user the section is expanded or collapsed.
My concern is when I tap section header then control goes to the gesture method. In that method how should I get the header cell to update the image accordingly?
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
if self.useSearchDefinitions {
if let ret = tableView.dequeueReusableCell(withIdentifier: INBOX_HEADER_CELL_IDENTIFIER) as? InboxHeaderCell {
ret.contentView.backgroundColor = UIColor(red: 236 / 255.0, green: 236 / 255.0, blue: 236 / 255.0, alpha: 1.0)
ret.contentView.tag = section
ret.lblHeaderTitle?.textColor = UIColor(red: 110 / 255.0, green: 110 / 255.0, blue: 110 / 255.0, alpha: 1.0)
ret.lblHeaderTitle?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
ret.lblHeaderTitle?.text = presenter.sectionTitle(section)
ret.accessoryImage.image = UIImage(named: "inbox-expand.png")
// Set tap gesture
let headerViewTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.headerViewGestureHandler))
headerViewTapRecognizer.delegate = self
headerViewTapRecognizer.numberOfTouchesRequired = 1
headerViewTapRecognizer.numberOfTapsRequired = 1
ret.contentView.addGestureRecognizer(headerViewTapRecognizer)
return ret.contentView
}
}
return nil
}
and this is to get the gesture
func headerViewGestureHandler(_ sender: UIGestureRecognizer)
{
tableView.beginUpdates()
if let tag = sender.view?.tag {
let section = Int(tag)
let shouldCollapse: Bool = !collapsedSections.contains((section))
let numOfRows = Int(presenter.numberOfRows(tag))
}
}
how should I get the particular clicked section header cell in this method so I can update the image accordingly?
Thanks in advance.

I would recommend:
put the Gesture code inside your section header
using a "call back" closure for passing the tap back to the view controller
Here is a simple example (assumes you have a View Controller with a Table View, hooked up via IBOutlet):
class SimpleSectionHeaderView: UITableViewHeaderFooterView, UIGestureRecognizerDelegate {
// typical UILabel
var lblHeaderTitle: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
// this is our "call back" closure
var headerTapCallback: (() -> ())?
func headerViewGestureHandler(_ sender: UIGestureRecognizer) {
// just for debugging, so we know the tap was triggered
print("tapped!!!")
// "call back" to the view controller
headerTapCallback?()
}
func commonInit() {
// set our backgroundColor
contentView.backgroundColor = .cyan
// add a label and set its constraints
self.addSubview(lblHeaderTitle)
lblHeaderTitle.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 8.0).isActive = true
lblHeaderTitle.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0.0).isActive = true
// Set tap gesture
let headerViewTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.headerViewGestureHandler))
headerViewTapRecognizer.delegate = self
headerViewTapRecognizer.numberOfTouchesRequired = 1
headerViewTapRecognizer.numberOfTapsRequired = 1
// add it to self
self.addGestureRecognizer(headerViewTapRecognizer)
}
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
}
class TableWithSectionHeadersViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var theTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// standard cell registration
theTableView.register(UITableViewCell.self, forCellReuseIdentifier: "reuseIdentifier")
theTableView.register(SimpleSectionHeaderView.self, forHeaderFooterViewReuseIdentifier: "simpleHeaderView")
// make sure these are set (in case we forgot in storyboard)
theTableView.delegate = self
theTableView.dataSource = self
}
func handleHeaderTap(_ section: Int) -> Void {
// do whatever we want based on which section header was tapped
print("View Controller received a \"tapped\" in header for section:", section)
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: "simpleHeaderView") as! SimpleSectionHeaderView
// set the view's label text
v.lblHeaderTitle.text = "Section \(section)"
// set the view's "call back" closure
v.headerTapCallback = {
_ in
self.handleHeaderTap(section)
}
return v
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60;
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell...
cell.textLabel?.text = "\(indexPath)"
return cell
}
}
This also eliminates any need to set any .tag properties (which is generally a bad idea, for a number of reasons).

Related

How to handle multiple buttons in a custom UITableViewCell?

I have a custom UITableViewCell which has 2 buttons (for incrementing and decrementing) and a count label in it. What I want to achieve is to update countLabel appropriately when subtractButton or addButton is tapped.
My custom cell class:
class ItemOptionCell: UITableViewCell {
private var count = 0
private var countLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.font = UIFont.systemFont(ofSize: 14)
label.numberOfLines = 0
label.text = "0"
label.adjustsFontSizeToFitWidth = true
return label
}()
private let subtractButton: UIButton = {
let subButton = UIButton(type: .system)
subButton.setTitle("-", for: .normal)
subButton.addTarget(self, action: #selector(decreaseItemCount), for: .touchUpInside)
return subButton
}()
private let addButton: UIButton = {
let addButton = UIButton(type: .system)
addButton.setTitle("+", for: .normal)
addButton.addTarget(self, action: #selector(increaseItemCount), for: .touchUpInside)
return addButton
}()
// contains subtract, add buttons and item count
private var operationsStackView = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .white
configureOperationsStackView()
}
func set(itemOption: ItemOption) {
itemLabel.text = itemOption.title
}
private func configureOperationsStackView() {
addSubview(operationsStackView)
// code for autolayout
}
#objc private func decreaseItemCount() {
if count > 0 {
count -= 1
}
updateCountLabel()
}
#objc private func increaseItemCount() {
count += 1
updateCountLabel()
}
private func updateCountLabel() {
countLabel.text = String(count)
}
Part of ViewController for handling table view delegates:
extension ItemOptionsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = UILabel()
label.text = "Header #1"
label.backgroundColor = .orange
return label
}
func numberOfSections(in tableView: UITableView) -> Int {
return options.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return options[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId) as! ItemOptionCell
let option = options[indexPath.section][indexPath.row]
cell.set(itemOption: ItemOption(title: option))
cell.selectionStyle = .none
return cell
}
}
All the forums I looked up only describe how to handle single buttons.
P.s. I read about assigning tags to buttons but found out that it's not a recommended way as when row count changes managing tags becomes problematic. Therefore, if possible, recommend a way with delegates or closures.
Use delegate for this purpose
protocol ItemOptionCellDelegate: AnyObject {
func didDecreaseItemTapped(in cell: ItemOptionCell)
func didIncreaseItemCount(in cell: ItemOptionCell)
}
class ItemOptionCell: UITableViewCell {
weak var delagate: ItemOptionCellDelegate?
...
private func decreaseItemCount() {
delegate?.didDecreaseItemTapped(in: self)
}
private func increaseItemCount() {
delegate?.didIncreaseItemCount(in: self)
}
in your ViewController
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId) as! ItemOptionCell
let option = options[indexPath.section][indexPath.row]
cell.set(itemOption: ItemOption(title: option))
cell.selectionStyle = .none
cell.delegate = self
cell.countLabel.text = //some value
return cell
}
extension ItemOptionsViewController: ItemOptionCellDelegate {
func didDecreaseItemTapped(in cell: ItemOptionCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
let option = options[indexPath.section][indexPath.row]
//do some stuff with your data, then reload table and set need value for count label.
}
func didIncreaseItemCount(in cell: ItemOptionCell) { ... }
Also, DON'T update countLabel inside cell implementation, basically you have to set value count from your model, for example in ViewController in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell func (in case you use MVC approach)
Forget about tags.
Create gesture recognisers in your cellForRowAt and assign them to the buttons:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
//Create gesture
//Create two of these: one will be for the add button and one for the minus one. Attach to eachh gesture a different function.
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
//Add the two different gestures to both of the buttons
cell.myButton.addGestureRecognizer(tap)
...
}
//Create two of these: one to add and one to subtract
#objc func tapped(){
print("ok")
//Perform your action here
}
Plus, when it comes to adding the buttons in the tableViewCell custom class, call contentView.addSubview(myButton) instead of addSubview(myButton).
Oh and obviously, remove all the stuff in your custom class where you add targets to buttons and stuff like that, only create and add the objects to the cell as I said before.
Edit:
Set your buttons as follows:
//Do this for both buttons
private let subtractButton: UIButton = {
let subButton = UIButton(type: .system)
subButton.setTitle("-", for: .normal)
return subButton
}()

Dynamic section header height on runtime

I have UITableView in view controller with a section header and in the header, I have one UITextView with scroll disabled and pinned all UITextView edges to its super view.
Here is the code for automatic height change
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CreatePostHeaderView") as? CreatePostHeaderView else {
return nil
}
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
}
And also set the estimated Height with this code
tableView.estimatedSectionHeaderHeight = 75
But on runtime when the text of UITextView exceeds height of 75, it doesn't change after that regardless of UITextView content. So, Do I need to add anything to make sure the table section header height changed according to UITextView content? Am I missing anything here?
When performing some action that changes the height of a cell (including header / footer cells), you have to inform the table view that the height has changed.
This is commonly done with either:
tableView.beginUpdates()
tableView.endUpdates()
or:
tableView.performBatchUpdates(_:completion:)
In this case, you want to call this when the text in your text view changes - easily done with a "callback" closure.
Here is an example of using a UITextView in a reusable UITableViewHeaderFooterView.
This will apply to loading a complex view from a XIB, but since this view is simple (only contains a UITextView), we'll do it all from code. This example uses 3 sections, each with 12 rows (default table view cells).
First, the table view controller class - no #IBOutlet or #IBAction connections, so just create a new UITableViewController and set its custom class to MyTestSectionHeaderTableViewController:
class MyTestSectionHeaderTableViewController: UITableViewController {
var myHeaderData: [String] = [
"Section 0",
"Section 1",
"Section 2",
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = 50
tableView.keyboardDismissMode = .onDrag
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 75
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "defCell")
tableView.register(MySectionHeaderView.self, forHeaderFooterViewReuseIdentifier: MySectionHeaderView.reuseIdentifier)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return myHeaderData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 12
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "defCell", for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: MySectionHeaderView.reuseIdentifier) as! MySectionHeaderView
v.myTextView.text = myHeaderData[section]
v.textChangedCallback = { txt in
self.myHeaderData[section] = txt
tableView.performBatchUpdates(nil, completion: nil)
}
return v
}
}
and this is the UITableViewHeaderFooterView class. Note that it needs to conform to UITextViewDelegate so we can tell the controller the text has changed (so it can update the height when needed), and we pass back the newly edited text to update our data source:
class MySectionHeaderView: UITableViewHeaderFooterView, UITextViewDelegate {
static let reuseIdentifier: String = String(describing: self)
var myTextView: UITextView = {
let v = UITextView()
v.isScrollEnabled = false
return v
}()
var textChangedCallback: ((String) -> ())?
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
contentView.addSubview(myTextView)
myTextView.translatesAutoresizingMaskIntoConstraints = false
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
myTextView.topAnchor.constraint(equalTo: g.topAnchor),
myTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
myTextView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
myTextView.bottomAnchor.constraint(equalTo: g.bottomAnchor)
])
myTextView.delegate = self
}
func textViewDidChange(_ textView: UITextView) {
guard let str = textView.text else {
return
}
textChangedCallback?(str)
}
}
The result:

Swift UITableView separator hidden until scroll

I have implemented a custom table view cell which appears but without the separator until you scroll. In the viewDidAppear I have set the separator style, the label border is not overlapping the cell edges. Help
Before Scrolling
After Scrolling
Larger Sim Window
On device testing
The pattern is MVVM with a custom cell.
Model
import Foundation
struct OAuthList {
let providers: [String]
init() {
self.providers = OAuthProviders.providers
}
}
View Model
import Foundation
struct OAuthListViewModel {
var providerList: [String]
init(providers: [String]) {
self.providerList = providers
}
}
LoginViewController
import UIKit
class LoginViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var providerButtons = OAuthListViewModel(providers: OAuthProviders.providers)
override func viewDidLoad() {
super.viewDidLoad()
let attributes = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17)]
self.navigationController?.navigationBar.titleTextAttributes = attributes
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.barTintColor = #colorLiteral(red: 1, green: 0.738589704, blue: 0.9438112974, alpha: 1)
self.navigationItem.title = "LOGIN / SIGNUP"
self.navigationItem.leftBarButtonItem?.tintColor = .white
self.navigationItem.leftBarButtonItem?.isEnabled = false
self.tableView.separatorColor = .white
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.register(CustomCell.self, forCellReuseIdentifier: TextCellIdentifier.textCellIdentifier)
self.tableView.layoutMargins = UIEdgeInsets.zero
self.tableView.separatorInset = UIEdgeInsets.zero
self.tableView.tableFooterView = UIView()
}
}
extension LoginViewController {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return providerButtons.providerList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TextCellIdentifier.textCellIdentifier, for: indexPath) as! CustomCell
let row = indexPath.row
cell.backgroundColor = #colorLiteral(red: 1, green: 0.738589704, blue: 0.9438112974, alpha: 1)
cell.buttonLabel.text = providerButtons.providerList[row]
cell.layoutMargins = UIEdgeInsets.zero
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(providerButtons.providerList[indexPath.row])
}
}
Custom Cell
class CustomCell: UITableViewCell {
var labelText: String?
var buttonLabel: UILabel = {
var label = UILabel()
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: TextCellIdentifier.textCellIdentifier)
self.addSubview(buttonLabel)
buttonLabel.translatesAutoresizingMaskIntoConstraints = false
buttonLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
buttonLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
buttonLabel.textColor = UIColor.white
}
override func layoutSubviews() {
if let labelText = labelText {
buttonLabel.text = labelText
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You can hide the separator and simply add UIView to act as a separator inside your custom cells
Enlarge your simulator window or try it on an actual device not sim.
When the sim window is small enough it can have a hard time displaying separators in a tableview since the separators are normally only 0.5pt in height.
A good indicator that the issue I mentioned above is occurring is when you scroll the tableview on the simulator and random separators start to appear while some are hidden. Which is what appears to be happening in your second screenshot.

Making custom UITableView Cells Display Contents Properly (Not Blank)

I have been working on making an iOS app which requires a screen/view that is scrollable and has an image, then a list and then an image and then another image (attached is the screenshot from the Android version I made)
Top of the view
View Scrolled
I have tried using the following code, which gives me the correct amount of cells but they are all blank.
//
// ServicesTableViewController.swift
// Contact Australis
//
// Created by Raghav Khanna on 22/4/18.
// Copyright © 2018 Australis. All rights reserved.
//
import UIKit
class ServiceViewCell: UITableViewCell {
#IBOutlet weak var IMage: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
IMage.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
class ServiceViewCellList: UITableViewCell {
#IBOutlet weak var somethin_label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let color = UIColor(red: 0/255, green: 105/255, blue: 191/255, alpha: 1.0).cgColor
let back_colour = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0).cgColor
let back_colour_ui = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0)
let radius: CGFloat = 5
let border_width:CGFloat = 1.5
somethin_label.layer.borderColor = color
somethin_label.layer.borderWidth = border_width
somethin_label.layer.cornerRadius = radius
somethin_label.backgroundColor = back_colour_ui
}
var items_maintenance = ["Painting","All Lighting & Globe Replacemt",
"Carpet & Hard Floor Replacement","Electrical Work & Maintenance","Plumbing Work & Maintenance","Test & Tag Completion","Office Furniture Removal", "Hard Waste Removal", "Window Frosting", "All Other Handy Man & Maintenance Tasks"]
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
} class ServiceViewCellCleaning: UITableViewCell {
#IBOutlet weak var Title: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
Title.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
class ServiceViewCellCleaningList: UITableViewCell {
#IBOutlet weak var other_label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let color = UIColor(red: 0/255, green: 105/255, blue: 191/255, alpha: 1.0).cgColor
let back_colour = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0).cgColor
let back_colour_ui = UIColor(red: 212/255, green: 242/255, blue: 253/255, alpha: 1.0)
let radius: CGFloat = 5
let border_width:CGFloat = 1.5
other_label.layer.borderColor = color
other_label.layer.borderWidth = border_width
other_label.layer.cornerRadius = radius
other_label.backgroundColor = back_colour_ui
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
class ServicesTableViewController: UITableViewController {
let basicCellIdentifier = "BasicCell"
var items_maintenance = ["Painting","All Lighting & Globe Replacement", "Carpet & Hard Floor Replacement","Electrical Work & Maintenance","Plumbing Work & Maintenance","Test & Tag Completion","Office Furniture Removal", "Hard Waste Removal", "Window Frosting", "All Other Handy Man & Maintenance Tasks"]
var items_cleaning = ["All Genral Comercial Cleaning","Office Cleaning", "Initial Clean","Spring Clean","Steam Carpet Cleaning","Window Washing","High Pressure Washing", "Waste Removal", "Strip & Seal Hard Floors", "Scrubbing & Buffing Hard Floors"]
let cellSpacingHeight: CGFloat = 5
#IBOutlet var table: UITableView!
func configureTableView() {
//tableView.rowHeight = UITableViewAutomaticDimension
//tableView.estimatedRowHeight = 1000.0
//let rect = CGRect(origin: .zero, size: CGSize(width: 400, height: 400))
//self.tableView = UITableView(frame: rect, style: UITableViewStyle.plain)
table.register(ServiceViewCell.self, forCellReuseIdentifier: "maintenance")
table.register(ServiceViewCellList.self, forCellReuseIdentifier: "customcell")
table.register(ServiceViewCellCleaning.self, forCellReuseIdentifier: "cleaning")
table.register(ServiceViewCellCleaningList.self, forCellReuseIdentifier: "cleaning_customcell")
}
/*func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return
}*/
override func viewDidLoad() {
super.viewDidLoad()
self.configureTableView()
table.reloadData()
table.delegate = self
table.dataSource = self
// Uncomment the following line to preserve selection between presentations
self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 4
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else if section == 1 {
return items_maintenance.count
} else if section == 2 {
return 1
}
else {
return items_cleaning.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let maintenance_title = table.dequeueReusableCell(withIdentifier: "maintenance", for: indexPath) as! ServiceViewCell
let maintenance_list = table.dequeueReusableCell(withIdentifier: "customcell", for: indexPath) as! ServiceViewCellList
let cleaning_title = table.dequeueReusableCell(withIdentifier: "cleaning", for: indexPath) as! ServiceViewCellCleaning
let cleaning_list = table.dequeueReusableCell(withIdentifier: "cleaning_customcell", for: indexPath) as! ServiceViewCellCleaningList
maintenance_list.somethin_label?.text = self.items_maintenance[indexPath.row]
maintenance_list.somethin_label?.adjustsFontSizeToFitWidth = false
maintenance_list.somethin_label?.font = UIFont.systemFont(ofSize: 10.0)
cleaning_list.other_label?.text = "test"
cleaning_list.other_label?.adjustsFontSizeToFitWidth = false
cleaning_list.other_label?.font = UIFont.systemFont(ofSize: 10.0)
cleaning_title.Title?.image = UIImage(named: "cleaning.png")
maintenance_title.IMage?.image = UIImage(named: "maintenance.png")
if indexPath.section == 0 {
return maintenance_title
} else if indexPath.section == 1 {
return maintenance_list
} else if indexPath.section == 2 {
return cleaning_title
}
else {
return cleaning_list
}
return cleaning_list
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
//Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return false
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
My Storyboard looks like this (Its another view in the main storyboard with for prototype cells with custom classes) and I am struggling to figure out why I keep getting either "Unexpectedly found nil while unwrapping an Optional value" for "maintenance_list.somethin_label!.text = self.items_maintenance[indexPath.row]" or this (blank cells) when I use '?' instead of '!'.
I know why I don't get the nil while unwrapping error when using the '?'. But the real problem is why I am not being able to interact with the views in each of the cells to display the desired data. I have checked all the outlets, and they are all correct.
Any help would be greatly appreciated.
Thanks in advance.
Without having access to the entire project is difficult to say why it's not working.
But I think the approach you are following is not correct, you should look into having only two sections (Maintenance, Cleaning) and then each item of Maintenance and Cleaning would be a cell, so your datasource should return 2 sections and 10 rows for each section.
You would need a section header, which would have an image view, and then only one prototype cell, that you can reuse for any row.
Hope this helps.

When I swipe a UITableViewCell, the header view moves as well

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
}

Resources