i want to disable click on particular Cell.it means, i want not to show highlight color(selection indication) when we touch on particular cell? any help please?
Either use cell.selectionStyle = UITableViewCellSelectionStyleNone; or return false or return null in the delegate method tableView:willSelectRowAtIndexPath.
Swift:
If you're using a custom cell:
class YourCustomCell: UITableViewCell {
override func awakeFromNib() {
setup()
}
init() {
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
fileprivate func setup() {
self.selectionStyle = .none
}
}
If you're not using a custom cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifierID")
cell.selectionStyle = .none
return cell
}
Bonus: If you want to hide the cell selection and also don't want to call func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {...} then only set cell.isUserInteractionEnabled = false. However, this is not a good practice.
Related
I made 4 identical cells with subviews by using UITableViewCell subclass 'FruitTableViewCell' class.
FruitTableViewCell.swift
class FruitTableViewCell: UITableViewCell, UITextFieldDelegate {
var fruitsTextField = UITextField()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(fruitsTextField)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
fruitsTextField.frame = CGRect(x: 100, y: 7.5, width: 50, height: 30)
fruitsTextField.backgroundColor = UIColor.darkGray
fruitsTextField.delegate = self
}
}
TableViewController.swift
class TableViewController: UITableViewController, UITextFieldDelegate {
let fruitsComponents: [String] = ["Apple", "Banana", "Grape", "Pear"]
let cellReuseidentifier = "cell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(FruitTableViewCell.self, forCellReuseIdentifier: cellReuseidentifier)
}
override func numberOfSections(in tableView: UITableView) -> Int {
sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruitsComponents.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseidentifier, for: indexPath) as! FruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
return cell
}
}
It works well.
But in fact, I want to add different accessories(or subviews) for each cell. Row 0 for UITextField, Row 1 for UILabel, Row 2 for Stepper, Row 3 for UILabel, ... and so on.
So I made the other UITableViewCell subclass 'AnotherFruitTableViewCell' class to use.
And I tried by using 'if' statement.
revised TableViewController.swift
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseidentifier, for: indexPath) as! FruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseidentifier, for: indexPath) as! AnotherFruitTableViewCell
cell.textLabel?.text = fruitsComponents[indexPath.row]
return cell
}
But the message 'could not cast value of type' poped up.
Because of this code, I think.
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(FruitTableViewCell.self, forCellReuseIdentifier: cellReuseidentifier)
}
And fundamentally, I think 'if' statement is not a good way to add different accessories for each cells.
How can I add different accessories(or subviews) for each cell?
You registered your FruitTableViewCell but not registered AnotherFruitTableViewCell
I have UITableView with 2 expanded Sections.
So, when I click on section's header, rows will be hidden:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
print("Section: \(indexPath.section), Row \(indexPath.row), \(sections[indexPath.section].expanded)")
if sections[indexPath.section].expanded {
return 88
} else {
return 0
}
}
I am adding an UIlabel to a cell in UITableView as below:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let label = UILabel()
cell.contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.leftAnchor.constraint(equalTo: cell.leftAnchor).isActive = true
label.topAnchor.constraint(equalTo: cell.topAnchor).isActive = true
label.widthAnchor.constraint(equalTo: cell.widthAnchor).isActive = true
label.heightAnchor.constraint(equalToConstant: 30).isActive = true
label.text = "row = \(indexPath.row)"
return cell
}
The above is working but cells are not properly showing the screen as sometime, the content of row1 is printed in another row.
If I add the below code, and when I click on a header of any section, the rows will be properly showing on the tableView, but just one time, I mean if I click one more time on the header, an error occurs inside CellForRowAt function (Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)):
cell.contentView.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
Any advise?
EDIT:
just to explain how the sections are expanded, i will add the following:
protocol ExpandableHeaderViewDelegate {
func toggleSection(header: ExpandableHeaderView, section: Int)
}
class ExpandableHeaderView: UITableViewHeaderFooterView {
var delegate: ExpandableHeaderViewDelegate?
var section: Int!
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(selectHeaderAction)))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func selectHeaderAction(gestureRecognizer: UITapGestureRecognizer) {
let cell = gestureRecognizer.view as! ExpandableHeaderView
delegate?.toggleSection(header: self, section: cell.section)
}
func toggleSection(header: ExpandableHeaderView, section: Int) {
tableView.beginUpdates()
tableView.endUpdates()
}
}
My question was solved by [Knight0fDragon].
extension UIView
{
func clearSubviews()
{
for subview in self.subviews as! [UIView] {
subview.removeFromSuperview();
}
}
}
and then
cell.contentView.clearSubviews()
Thanks for all kind people for their support.
If the problem is just for mismatch of cell data then try using
override func prepareForReuse() {
super.prepareForReuse()
self.label.text = ""
}
Declare the UILabel in the cell's class with null value instead of declaring it in cellForRowAt
Try this inside cellForRowAt:
if tableView.rectForRow(at: indexPath).height == 0.0 {
return UITableViewCell()
}
It should be above all other code.
Try (call it before adding new label)
cell.contentView.subviews.removeAll()
instead of
cell.contentView.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
I have a UITableViewCell that I want to show as selected when the UIViewController is presented. vc.tableView.selectRow:atIndexPath is nice in theory but it bypasses the calls for willSelect and didSelect on the cell.
The cell has an exposed UIImageView that setSelected toggles, which is what I'm trying to show on the initial load.
Any help here would be appreciated. Thanks!
I'll give you an example of how change background color of your cells and select a initial one, so you can follow and put the code that you need:
In your UIViewController subclass, implement these methods, so you can put your logic for selected and deselected states:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)!
selectedCell.backgroundColor = UIColor.purple
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let deselectedCell = tableView.cellForRow(at: indexPath)!
deselectedCell.backgroundColor = UIColor.clear
}
In your custom UITableViewCell subclass you have to override the isSelected property, this is the key to avoid the method tableView.selectRow:atIndexPath bypassing didSelect:
class CustomTableViewCell: UITableViewCell {
override var isSelected: Bool {
didSet {
print(isSelected.description)
self.selectionStyle = .none
if isSelected{
self.backgroundColor = UIColor.purple
} else {
self.backgroundColor = UIColor.clear
}
}
}
}
Last, back to your UIViewController subclass, you can call selectRow:atIndexPath in your viewDidLoad method, for instance:
override func viewDidLoad(){
super.viewDidLoad()
tableView.selectRow(at: IndexPath(row: 0, section: 0) , animated: true, scrollPosition: UITableViewScrollPosition.none)
}
converting my code to swift ONLY so no use of storyboards. After rewriting the code (no storyboard usage) the header information is NOT shown !!
If I change the viewForHeaderInSection return value from:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerId") as! ListsVCHeader
cell.fillHeader(header: listTitles[section])
return cell.contentView
}
to:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerId") as! ListsVCHeader
cell.fillHeader(header: listTitles[section])
return cell // ==> removed .contectView
}
the header content is shown BUT the section header moves left performing a swipe gesture that I use to initiate a row delete. Any suggestions? Find the relevant code below.
class ListsVC: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(ListsVCHeader.self, forCellReuseIdentifier: "headerId")
// register other Cells
}
override func numberOfSections(in tableView: UITableView) -> Int {
return listTitles.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int)
-> String? {
return listTitles[section]
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerId") as! ListsVCHeader
cell.fillHeader(header: listTitles[section])
return cell.contentView
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return HeaderSectionHeight
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
switch editingStyle {
case .delete:
// mycode
default:
break
}
}
}
the Section Header cell
class ListsVCHeader: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// add subviews and constraints
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func fillHeader (header: String) {
name.text = header
}
}
Change your register in viewDidLoad to:
tableView.register(ListsVCHeader.self, forHeaderFooterViewReuseIdentifier: "headerId")
Your class ListsVCHeader should be changed to:
class ListsVCHeader: UITableViewHeaderFooterView
and last change the following code:
let cell = tableView.dequeueReusableCell(withIdentifier: "headerId") as! ListsVCHeader
to
let cell = tableView.dequeueReusableHeaderFooterView(withIdentifier: "headerId) as! ListsVCHeader
the cell is very simple, just a textLabel. i want to tap the cell and then print some words. this is a test . but the didSelectRowAtIndexPath is not work. Please tell me why, thank you !
import UIKit
import SnapKit
class ListTableViewController : UITableViewController{
var itemData: ListTableModel!
var header: UIView!
override init(style: UITableViewStyle){
super.init(style: style)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
itemData = ListTableModel()
self.tableView.dataSource = self
self.tableView.delegate = self
}
....
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("mCell", forIndexPath: indexPath)
cell.textLabel?.userInteractionEnabled = false
let listCell = cell as! ListTableViewCell
if let item = itemData.getItemData(indexPath.row) {
listCell.textLabel!.text = item.valueForKey("title") as? String
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("indexPathS")
}
}
Please check if self.tableView.allowsSelection = NO is not written anywhere. And in the storyboard, check if single selection is checked under Selection tab.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let text:NSString = myArray .objectAtIndex(indexPath.row) as! NSString
delegate?.myMethod(text as String)
self.navigationController?.popToRootViewControllerAnimated(true)
}
you can try this.