So I'm setting a UITableViewCell's layout programmatically when it is selected:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedCellIndexPath = indexPath
var selectedCell = tableView.cellForRowAtIndexPath(indexPath)!
tableView.beginUpdates()
tableView.endUpdates()
var cell:SelectedPatientCell = tableView.dequeueReusableCellWithIdentifier("patient selected", forIndexPath: indexPath) as! SelectedPatientCell
cell.patientName.text = patients[indexPath.row].fullName
cell.dob.text = patients[indexPath.row].dob
...
selectedCell = cell
}
And when I scroll the tableView, the layout of the cell resets to its original layout set in cellForRowAtIndexPath. However, the height stays as it should when I set it in the function above. Does anyone know how to fix this?
Here is an album of what's happening:
http://imgur.com/a/OUIMJ
Image 1:original state
Image 2: selected state (how it should stay on scrolling)
Image 3: what actually happens
you should hold this state in
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath == self.selectedCellIndexPath {
var cell:SelectedPatientCell = tableView.dequeueReusableCellWithIdentifier("patient selected", forIndexPath: indexPath) as! SelectedPatientCell
cell.patientName.text = patients[indexPath.row].fullName
cell.dob.text = patients[indexPath.row].dob
return cell
}
let cell = tableView.dequeueReusableCellWithIdentifier("patient selected") as! OriCell
...
return cell
}
in this way if you scroll tableView,it won't resume to original Cell.
Hopefully it is clear.
So I found my own solution:
Instead of doing
tableView.beginUpdates()
tableView.endUpdates()
I needed to do this at the end of the function:
tableView.reloadData()
and that solves the issue
Related
I have a tableview in my storyboard where the prototype cell has a disclosure indicator by default.
When I populate my table I want to remove the indicator only from the last cell AND center a spinner on it.
I'm doing it like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CharacterCell", for: indexPath) as! CharacterCell
if indexPath.row == charactersViewModel.charactersCount - 1 {
cell.accessoryType = .none
cell.accessoryView = .none
// Spinner
let spinner = UIActivityIndicatorView(style: .large)
spinner.color = .white
spinner.center = cell.contentView.center
cell.contentView.addSubview(spinner)
spinner.startAnimating()
}
return cell
}
The problem is that the spinner is offcenter, a little bit to the left, just like if the accessory is still there, but hidden.
I feel maybe I'm missing the lifecycle of a table cell, maybe it's getting the center value of the content view when the accessory is still there, so when it's removed it is offcenter?
I tried on willDisplay as well but the same thing happens.
Any tips on this?
As #Paulw11 mentioned, I used a second subclass and created another cell prototype in my tableview.
Then when the last position at the table is reached, we can use the second prototype on cellForRowAt.
Here how it is:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row >= charactersViewModel.charactersCount - 1 {
reloadRows(indexPath: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingCharacterCell", for: indexPath) as! LoadingCharacterCell
cell.startSpinner()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CharacterCell", for: indexPath) as! CharacterCell
cell.configureCell(charactersViewModel: charactersViewModel, cell: cell, index: indexPath.row)
return cell
}
}
private func reloadRows(indexPath: IndexPath) {
var indexPathList = [IndexPath]()
indexPathList.append(indexPath)
charactersTableView.reloadRows(at: indexPathList, with: .automatic)
}
And with the reloadRows function, the last cell is updated and removed when the table receives more data.
No article explains it clearly regarding my query, I have three cells in a static table and I want to hide second cell when users taps on first cell. Any kind of help is appreciated.
Although you cannot stop the static table from trying to show your cells, you can set their height to zero, making them effectively invisible:
Add this method to your table view controller delegate class:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)
return cell == myHiddenCell ? 0 : super.tableView(tableView, heightForRowAtIndexPath:indexPath)
}
In the didSelectCellAtIndexPath method, you can set the height to 0 to hide it :
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
let indexPath = NSIndexPath(forItem: 1, inSection : 0)
let secondCell = tableview.cellForRowAtIndexPath(indexPath)
secondCell.frame.size.height = 0;
self.view.layoutSubviews()
}
}
If you want an animation, just put self.view.layoutSubviews() in an UIView animation method UIView.animateWithDuration... etc
For me, setting the height to 0 for some cells and another height for other cells wasn't an option, as all my cells have different height.
I created another cell in Storyboard, and set row height of 0 (in size inspector). Then in the code, I show the cell with height = 0 if I want to hide it, if not, I show the other cell:
if (hideCell) {
let hiddenCell = tableView.dequeueReusableCell(withIdentifier: "hiddenCell",for: indexPath) as! TheWallTableViewCell
return hiddenCell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",for: indexPath) as! TheWallTableViewCell
return cell
}
I have the strangest glitch after I select a UITableView cell and scroll away(You can see it here):
When I select a cell, it's programmed to change its text and change the font color from brown to red. However, when I scroll, other cells that I have not selected change their font color to red. And when I scroll back to the selected cell it reverts to its original text and sometimes, its font color too (from red to brown).
I've used this post in attempt to fix it. But still the glitch remains.
I am completely baffled as to why this is happening and would love love love if anyone could tell me why.
In my code I made my ViewController CategoryViewController the UITableView's Datasource & Delegate instead of a UITableViewController b/c I have other views in my CategoryViewController, not just a UITableView
class CategoryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private let themeColors = ThemeColors()
private let expensesOrganizer = ExpensesOrganizer()
override func viewDidLoad() {
super.viewDidLoad()
//Set up subCategory table view
subCategoryTableView.dataSource = self
subCategoryTableView.delegate = self
}
// MARK: UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return expensesOrganizer.getNumOfSubcategoriesFor(category!)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let subcategoryCell = tableView.dequeueReusableCellWithIdentifier("subCategoryCell", forIndexPath: indexPath) as! SubcategoryTableViewCell
let subcategory = expensesOrganizer.getSubcategoryFor(category!, index: indexPath.row)
subcategoryCell.subCategoryLabel.text = "\(indexPath.row) \(expensesOrganizer.getText(subcategory.rawValue))"
subcategoryCell.selectedBackgroundView = UIView(frame: CGRect.zero)
subcategoryCell.selectedBackgroundView?.backgroundColor = themeColors.getColorOfCategory(category!)
return subcategoryCell
}
// MARK: UITableViewDelegate
var indexPathSelectedCell: NSIndexPath?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let subcategoryCell = tableView.cellForRowAtIndexPath(indexPath) as! SubcategoryTableViewCell
subcategoryCell.subCategoryLabel.textColor = UIColor.redColor()
subcategoryCell.subCategoryLabel.text = "\(indexPath.row) didSELECTRowAtIndexPath called"
indexPathSelectedCell = indexPath
//What the post said to add:
let selectedRows = subCategoryTableView.indexPathsForSelectedRows
for i in selectedRows! {
if !i.isEqual(indexPath){
subCategoryTableView.deselectRowAtIndexPath(i, animated: false)
}
}
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let subcategoryCell = tableView.cellForRowAtIndexPath(indexPath) as! SubcategoryTableViewCell
subcategoryCell.subCategoryLabel.textColor = themeColors.getFontColor(Shade.Light)
subcategoryCell.subCategoryLabel.text = "\(indexPath.row) didDESELECTRowAtIndexPath called"
}
The approach that you take is incorrect, because you are not setting the color when you reuse a cell. Your cellForRowAtIndexPath needs to set color back to brown if the cell is not selected. It should be set to red if the cell is selected:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let subcategoryCell = tableView.dequeueReusableCellWithIdentifier("subCategoryCell", forIndexPath: indexPath) as! SubcategoryTableViewCell
let subcategory = expensesOrganizer.getSubcategoryFor(category!, index: indexPath.row)
subcategoryCell.subCategoryLabel.text = "\(indexPath.row) \(expensesOrganizer.getText(subcategory.rawValue))"
subcategoryCell.selectedBackgroundView = UIView(frame: CGRect.zero)
subcategoryCell.selectedBackgroundView?.backgroundColor = themeColors.getColorOfCategory(category!)
if let selected = tableView.indexPathsForSelectedRows() as? [NSIndexPath] && selected! == indexPath {
subcategoryCell.subCategoryLabel.textColor = UIColor.brownColor()
} else {
subcategoryCell.subCategoryLabel.textColor = UIColor.redColor()
}
return subcategoryCell
}
This has to do with cell reuse.
When you change the color of the label in didSelectRowAtIndexPath, then scroll that cell off-screen, it gets reused for a different cell that will appear on-screen.
However, since you don't prepare the cell for reuse, it is still using the selected font color for your label.
Assigning the label's default text color in prepareForReuse or cellForRowAtIndexPath will fix this issue.
I have multiple UICollectionViewCells. When the user taps on a specific cell, I would like my app to change the background image of the touched cell.
My approach is to focus on the didSelectItemAtIndexPath method. When a cell is touched, this method will be called.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var cell: UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("cellIdentifier", forIndexPath: indexPath) as! UICollectionViewCell
cell.backgroundView = UIImageView(image: UIImage(named: "myImage"))
}
However, I can't get it working but I don't know why. The issue is probably related to indexPath, that doesn't return a correct value of the cell. I tried using indexPath.row and this does actually return an Int number of the cell.
What's more, I'm also creating a new UICollectionViewCell with var but this cell already exists.
Why isn't the cell updating its background image? How do I change the background image of a UICollectionViewCell that has been touched by the user?
I totally agree with the Josh's answer, but if you change the background image using the didSelectItemAtIndexPath method it works fine as well. Then, you can use the cellForRowAtIndexPath method that returns the UITableViewCell at the specified indexPath, like in the following way:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var cell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
cell.backgroundView = UIImageView(image: UIImage(named: "photo2"))
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
cell.backgroundView = UIImageView(image: UIImage(named: "photo1"))
cell.selectionStyle = .None
return cell
}
I just put the selectionStyle to .None to avoid the highlight. I hope this help you.
Right now I have a list of scrolling usernames using a Collection View of buttons. But I’d like to add overlapping delete buttons to each row. They'd need to be attached to the name buttons and scroll with them.
How can I add these buttons to my CollectionView?
(Also I'd like to skip the delete button on the first row for obvious reasons)
Current Code:
//Add the cells to collection
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UsernameCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UsernameCollectionViewCell
cell.usernameLabel.text = userNames [indexPath.row]
return cell
}
//Upon Selecting an item
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if (indexPath.row == 0){
self.performSegueWithIdentifier("newUserSegue", sender: self)
}
else {
sendData(userNames[indexPath.row])
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Got it working! Here's how:
I added a button to the cell in the Storyboard.
Connected an outlet to the UICollectionViewCell class.
Edited view controller code to:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UsernameCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UsernameCollectionViewCell
cell.usernameLabel.text = userNames [indexPath.row]
cell.deleteButton?.layer.setValue(indexPath.row, forKey: "index")
cell.deleteButton?.addTarget(self, action: "deleteUser:", forControlEvents: UIControlEvents.TouchUpInside)
// Remove the button from the first cell
if (indexPath.row == 0){
var close : UIButton = cell.viewWithTag(11) as! UIButton
close.hidden = true
}
return cell
}
func deleteUser(sender:UIButton) {
let i : Int = (sender.layer.valueForKey("index")) as! Int
userNames.removeAtIndex(i)
UserSelectCollection.reloadData()
}
Many thanks to JigarM for his examples on GitHub:
https://github.com/JigarM/UICollectionView-Swift
Why not create custom UICollectionViewCell in IB and just add button to it ?
Register it to your collectionView with :
- registerNib:forCellReuseIdentifier:
You can use delegate or notification to process button tap.