So the issue is when a cell is tapped, desired data is shown and when again tapped on same cell ( again desired data is shown.)
But when one cell is selected and we again select other cell (then the data is been shown of second tapped cell but the first one is not deselected).
How can I take care of this issue?
var selectedIndex = -1
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIView.animate(withDuration: 1) {
self.labelViewHeightConstraint.constant = 60
self.labelLeadingConstraint.constant = 136
self.view.layoutIfNeeded()
}
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
if(selectedIndex == indexPath.row) {
selectedIndex = -1
print("deselect")
UIView.animate(withDuration: 0.4) {
cell.secondView.isHidden = true
cell.firstView.backgroundColor = UIColor(red: 0.8588, green: 0.84705, blue: 0.8745, alpha: 1.0)
}
} else {
cell.secondView.isHidden = false
}
self.expandTableView.beginUpdates()
//self.expandTableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic )
self.expandTableView.endUpdates()
}
You can archive single selection by setting tableView property like belwo
tableView.allowsMultipleSelection = false
This can also be done from Attributes Inspector
Hope this helps
you must disable multiple selection by,
self.tbl.allowsMultipleSelection = false
and enable single selection by,
self.tbl.allowsSelection = true
EDIT:-
if you want to access your old (selected cells), you should make a call like this,
//first assign tag or indexPath in Cell,
cell.tag = indexPath.row
// or
cell.indexPath = indexPath
//then fetch like bellow,
let visibleCell = tableView.visibleCells.filter({$0.tag == self.selectedIndex})
//or
let visibleCell = tableView.visibleCells.filter({$0.indexPath.row == self.selectedIndex})
//if you use ,
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
//then it will get you new cell object.
Related
So I selected a cell in tableview.
it expands, shows detail text of the that cell then I started scrolling down till this selected cell goes out of view.
because of reuse identifier other cell in the the view get some the property of the selected cell automatically.
Is there any way to handle this ?
var selectedIndex = -1
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIView.animate(withDuration: 1) {
self.labelViewHeightConstraint.constant = 60
self.labelLeadingConstraint.constant = 136
self.view.layoutIfNeeded()
}
let cell = tableView.cellForRow(at: indexPath) as! CustomCell
if(selectedIndex == indexPath.row) {
selectedIndex = -1
print("deselect")
UIView.animate(withDuration: 0.4) {
cell.secondView.isHidden = true
cell.firstView.backgroundColor = UIColor(red: 0.8588, green: 0.84705, blue: 0.8745, alpha: 1.0)
}
} else {
cell.secondView.isHidden = false
}
self.expandTableView.beginUpdates()
//self.expandTableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic )
self.expandTableView.endUpdates()
}
And i have deselect tableview function as
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPathaddres , animated: true)
if let cell = tableView.cellForRow(at: indexPath) as? customCell {
cell.firstView.backgroundColor = UIColor(red: 0.8588, green: 0.84705, blue: 0.8745, alpha: 1.0)
print("deselected row")
}
}
I have already disabled multiple selection.
I am attaching the 2 screenshot.
I 1 is select first cell of tableview then scroll down https://drive.google.com/file/d/1RZlya_eVVjDzj02GKV9h0qJU8F29xMin/view?usp=drivesdk once that cell goes out of scope.
Jump server 3 (as seen in this screenshot gets selected) https://drive.google.com/file/d/15k4gLUPkgB6jGZ7AWR6km0Jajst9KKxM/view?usp=drivesdk get selected
Since tableview reuses its cells, you need to do some extra checks if you want your one cell to be different from the others.
Reset the cell to its default in prepareForReuse method. Like
hiding your view and reset the arrow direction in your case.
Check for selected index in your cellForRow method and expand your view like you do in your didSelectRow method and hide it if its not selected just as you do in your didDeselect method.
What I want to ask you is "Can one UITableviewcell be used for multiple tableview like viewholder that can use anytime for recyclerview in android?" what I used to do is in one viewcontroller I have a tableview with a custom Cell and gave its identifier as normal but if I trying to use another uitableview in another Viewcontroller with that cell that inside the previous tableview, it always gives me a blank cell with white background. is there a way to use it like that?
EDIT: Here is what my tableview look like when i've already set cellforrow for it already.
Click to view and here what my cell look like Click to view cell and here are my code for different cell in a tableview, It'll work if i use use those 2 cell in current tableview
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0{
let cell = self.mytable.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HistoryItemTableCell
let view = UIView(frame: CGRect(x: 0, y: cell.frame.maxY, width: cell.frame.size.width, height: cell.frame.size.height))
view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
cell.selectedBackgroundView = view
let order = OrderItemObj
cell.num_of_day.text = "\(order.ticket_type.name)"
cell.ref_num.text = order.order_tx_number
cell.quantity.text = order.number_tickets
cell.price.text = "$\(order.ticket_type.price).00 USD"
if order.status == "unpaid"{
cell.ic_status.image = UIImage(named: "ic_status_unpaid")
}else{
cell.ic_status.image = UIImage(named: "ic_status_paid")
}
cell.start_date.text = "\(order.start_date)"
cell.end_date.text = "\(order.expired_date)"
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! OrderDetailTicketCell
let t = listTicket[indexPath.row]
cell.dob.text = t.dob
cell.gender.text = t.sex
cell.nation.text = t.nationality
let url = URL(string: t.photo)
cell.imageN.kf.setImage(with: url)
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 3
}else{
return self.listTicket.count
}
}
override func viewDidLoad(){
super.viewDidLoad()
mytable.register(HistoryItemTableCell.self, forCellReuseIdentifier: "cell")
ViewHistoryItem()
mytable.dataSource = self
mytable.delegate = self
}
Yes you can. You have to register it again for the new tableView. It is just like how you create variables using the same type. This is also a class which can be used to create objects. Doesn't matter where you want to use it.
On the other hand if you are asking if instances of the same cell which are present in a tableView can be reused in another tableView, then the answer is no, because they have only been registered for that particular tableView.
Good Morning
I created a custom TableViewCell, which internally has a CollectionViewCell and a directional arrow. I was able to make the cell expand and collapse however I would like it when I expand one cell and I click on another one the previous collapse. Example: If cell is expanded and cell is tapped, I want cell to contract and cell to expand at the same time. So that only one cell can be in its expanded state in any given moment changing the targeting arrow.
Example:
If cellA is expanded and cellB is tapped, I want cellA to contract and cellB to expand at the same time. So that only one cell can be in its expanded state in any given moment changing the targetting arrow.
My code:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch(selectedIndexPath)
{
case nil:
let cell = myTableView.cellForRow(at: indexPath) as! TableViewCell
cell.arrowImagem.image = UIImage(named:"content_arrow2")
selectedIndexPath = indexPath
default:
if selectedIndexPath! == indexPath
{
let cell = myTableView.cellForRow(at: indexPath) as! TableViewCell
cell.arrowImagem.image = UIImage(named:"content_arrow")
//cell.reloadInputViews()
selectedIndexPath = nil
}
}
self.myTableView.beginUpdates()
self.myTableView.reloadData()
//myTableView.reloadRows(at: [indexPath], with: .automatic)
self.myTableView.endUpdates()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let index = indexPath
if selectedIndexPath != nil{
if(index == selectedIndexPath)
{
return 147
}
else{
return 67
}
}
else
{
return 67
}
}
You can do that but you need to do some coding. First of all detect when a row is selected (keep the Index Path of the selected row in a variable). When a row is selected you will add the index path of the new selected row and that of the previously selected (that you have in the variable) in an array that you pass to the tableView reloadRows from inside didSelectRowAt, this function will force redrawing of the two rows and the heightForRowAt function will be called to get the height of both rows, in heightForRowAt you will check the currently selected row (do you remember the variable I mentioned earlier?) and return the expanded height for that, while for the others you return the collapsed height. You should get the result you want. Hope that will help.
You can implement most of the functionality in the didSet handler:
var selectedIndexPath: IndexPath? {
didSet {
guard indexPath != oldValue else {
return
}
if let oldValue = oldValue {
let cell = myTableView.cellForRow(at: indexPath) as? TableViewCell
cell?.arrowImagem.image = UIImage(named:"content_arrow")
}
if let indexPath = indexPath {
let cell = myTableView.cellForRow(at: indexPath) as? TableViewCell
cell?.arrowImagem.image = UIImage(named:"content_arrow2")
}
let rowsToUpdate = [indexPath, oldValue].compactMap { $0 }
self.myTableView.beginUpdates()
// I don't think you actually need to reload rows here but keeping your code here...
self.myTableView.reloadRows(at: rowsToUpdate, with: .automatic)
self.myTableView.endUpdates()
}
}
and then in your didSelect:
if indexPath == selectedIndexPath {
selectedIndexPath = nil
} else {
selectedIndexPath = indexPath
}
(written without testing)
I have a ViewController that has a TableView. The tableView cell has a StackView with two images in it and outside it a label.
In the tableView:cellForRowAtIndexPath I get the cell and my data is inside a arrayList. And this is what I do:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "NamesTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NamesTableViewCell
let _name = _Names[indexPath.row]
if indexPath.row % 2 != 0 {
cell.backgroundColor = UIColor(hexString: "ededed")
}
cell.labelName.text = _name.name
if _name.gender! == "M" {
cell.pinkCircleImageView.hidden = true
} else if _name.gender! == "F" {
cell.blueCircleImageView.hidden = true
}
return cell
}
So as you can see I hide the images depending on the gender of the name and also change the background of every other cell.
Now, the behavior I am seeing is:
https://gyazo.com/1b2d39696892b7fb2f15b71696d9a925
The gender is available for every object, I've checked.
What do you guys think? Thanks!
cell.pinkCircleImageView.hidden = false
cell.blueCircleImageView.hidden = false
if _name.gender! == "M" {
cell.pinkCircleImageView.hidden = true
cell.bringSubviewToFront(blueCircleImageView)
} else if _name.gender! == "F" {
cell.blueCircleImageView.hidden = true
cell.bringSubviewToFront(pinkCircleImageView)
}
if indexPath.row % 2 == 0
{
cell.backgroundColor=UIColor.whiteColor()
}
else
{
cell.backgroundColor=UIColor(red: 248/255, green: 248/255, blue: 248/255, alpha: 1.0)
}
I need to present a UIStepper in a row of a UITableView(only the second row - see the image below).
Therefore I implemented func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell like below:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("OptionCell")!
let debugModeOptionType = DebugModeOptionsType(rawValue: indexPath.row)
switch(debugModeOptionType!) {
case .DummyCurrentLocation:
cell.textLabel!.text = "Dummy current location"
case .StepLength:
cell.textLabel!.text = "Step Length: \(stepLength)"
// create a UIStepper
let stepper = UIStepper(frame: CGRectMake(220, 10, 100, 10))
// customize UIStepper
stepper.autorepeat = true
stepper.value = stepLength
stepper.minimumValue = 0.1
stepper.stepValue = 0.02
stepper.maximumValue = 1.5
stepper.addTarget(self, action: #selector(adjustStepLength(_:)), forControlEvents: UIControlEvents.AllEvents)
// add UIStepper into the cell
stepper.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(stepper)
case .TrueHeading:
cell.textLabel?.text = "True Heading: \(trueHeading)"
case .MagneticHeading:
cell.textLabel?.text = "Magnetic Heading: \(magneticHeading)"
case .HeadingAccuracy:
cell.textLabel?.text = "Heading Accuracy: \(headingAccuracy)"
case .CurrentDirection:
cell.textLabel?.text = "Current Direction: \(currentDirection)"
case .DrawWalking:
cell.textLabel?.text = "Draw walking while navigating"
}
if selectedDebugModeOptions.contains(debugModeOptionType!) {
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
} else {
cell.accessoryType = UITableViewCellAccessoryType.None
}
return cell
}
However when I touch the UIStepper on a real device(this does not happen inside the simulator) the following happens:
When this happens, the other cells' UISteppers start flashing as well. Why does such a problem occurs?
I can't say why this happening only on a real device, but because table view cells are reused, you have to be careful when adding elements to the cells programmatically, because those elements (such as your stepper) will be spread to other cells as the cells are reused.
There are (at least) two ways you can deal with this:
Check for the presence of a stepper after you dequeue a reusable cell and remove it if it is on a row that doesn't need a stepper. You could do this by giving the stepper a unique tag number (such as 123) and then search for subviews with that tag and remove them.
let stepperTagNumber = 123
let cell = tableView.dequeueReusableCellWithIdentifier("OptionCell")!
let debugModeOptionType = DebugModeOptionsType(rawValue: indexPath.row)
if let stepper = cell.contentView.viewWithTag(stepperTagNumber) {
// We have a stepper but don't need it, so remove it.
if debugModeOptionType != .StepLength {
stepper.removeFromSuperview()
}
} else {
// We don't have a stepper, but need one.
if debugModeOptionType == .StepLength {
// create a UIStepper
let stepper = UIStepper(frame: CGRectMake(220, 10, 100, 10))
stepper.tag = stepperTagNumber // This is key, don't forget to set the tag
// customize UIStepper
stepper.autorepeat = true
stepper.value = stepLength
stepper.minimumValue = 0.1
stepper.stepValue = 0.02
stepper.maximumValue = 1.5
stepper.addTarget(self, action: #selector(adjustStepLength(_:)), forControlEvents: UIControlEvents.AllEvents)
// add UIStepper into the cell
stepper.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(stepper)
}
}
OR:
Create a second prototype cell for your tableview (called "OptionCellWithStepper"). Add the stepper to that cell in your storyboard. Then, when you call dequeueReusableCellWithIdentifier, use "OptionCellWithStepper" for case .StepLength and use identifier "OptionCell" for all the other cases. Doing it this way, you don't have to programmatically add the stepper, and you don't have to remember to remove it for the other cells.
let debugModeOptionType = DebugModeOptionsType(rawValue: indexPath.row)
let cellID = (debugModeOptionType == .StepLength) ? "OptionCellWithStepper" : "OptionCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellID)!