I have collection view with 3 cells. Each cell contains one button. When I tap on the button in the first cell it starts animate itself. So my gaol is to dismiss animation in the current button if I tap for example on the button in the second/3rd cell and vice verse.
What is the better way to do it. I suppose to store maybe all buttons in some array and then check which one is active now and then switch off it and switch on other.
But maybe it's better to create some cell builder or smth like that.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(VAXSettingsModeCell.reusableCellIdentifier(), forIndexPath: indexPath) as! VAXSettingsModeCell
cell.delegate = self
let title = modesStrings[indexPath.row]
cell.AnimatableButton.setTitle(title, forState: UIControlState.Normal)
}
You can store IndexPath of animated cell inside your ViewController
#interface MyViewController: ViewController
#property (strong, nonatomic) NSIndexPath *animatedCellIndexPath;
#end
When user tap the button inside cell you can save this cell's indexPath and stop animations for all visible cells and then start animation for tapped cell.
- (void)buttonTappedForCell:(UICollectionViewCell *)tappedCell {
self.animatedCellIndexPath = [self.collectionView indexPathForCell:tappedCell];
for (UITableViewCell *cell in [self.collectionView visibleCells]) {
[cell setAnimating:NO];
}
[tappedCell setAnimating:YES];
}
And for every new cell you can check if that cell must be animated (for example if user scrolls collection view)
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
cell = ...
[cell setAnimating:indexPath == self.animatedCellIndexPath];
return cell;
}
Create UICollectionViewCell reference on the animating cell and when you press on other cells (didSelectCell..) stop the animation on that cell. When you animating other cell change the reference to the animating cell. Have in mind that if you are using reuseIdentifier and the animating cell is not showing in the screen then the UICollectionView will reuse it and you might see the animating cell on the wrong position. In that case your solution with array will be the ideal but you have to track that the animating cell is not showing in the screen (or in the cellForRowAtIndexPath stop the animation on each cell and then check if the currentIndex path has to animate and start animating).
Update
You can do it without array. Just set an NSInteger activeCellIndex=indexPath.row and check that one.
I want to be able to change the color of a single UITableView cell. In my tableView(editActionsForRowAtIndexPath) I can swipe the cell and select a button to change the background color but when I scroll the cell off screen it changes back. How do I get it to retain the color? Thanks
Assuming you have only one section in your tableView with many rows, you need to do this in your tableView:cellForRowAtIndexPath: method:
if (indexPath.row == coloredCellIndex) {
cell.backgroundColor = UIColor.RedColor()
} else {
cell.backgroundColor = UIColor.WhiteColor()
}
You need to set the variable coloredCellIndex anywhere outside of this function, for example in viewDidLoad
Sorry, I can only write my answer in Objective-C. But I hope you can get my point.
If you are using a custom prototype cell, then you can add a property to your custom table view cell class, and store the current color you want for that cell, and then later in reload that color.
Sample code:
In CustomTableViewCell.h
#interface CustomTableViewCell : UITableViewCell
#property (nonatomic) UIColor *cellColor;
- (void)reloadBackgroundColor
#end
In CustomTableViewCell.m
- (void)reloadBackgroundColor
{
self.backgroundColor = cellColor; //this is just a sample.
}
And then under you can just set that color everytime the cell is reloaded.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
CustomTableViewCell *tableCell = (CustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"CustomTableCellIdentifier"];
[tableCell reloadBackgroundColor];
return tableCell;
Now, everytime you scroll the table, the color you want to set to that cell will be loaded.
If you are not using a custom prototype cell, then I suggest that you store the color you want for each cell in a Dictionary, with its index as the key. Then you just assign it again to that cell every time it is reloaded.
I hope this helps you. :)
Swift 3
viewDidLoad
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
cellForRowAt
cell.backgroundColor = .black
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:CustomTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "myCell") as! CustomTableViewCell
cell.accessoryType = .none
cell.backgroundColor = .black
return cell
}
How can I disable all cells, except the first 3 cells of a UITableView?
This returns nil if one of the disabled cells is selected:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ( indexPath.row >= 3 ) {
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]] setAlpha:0.5];
return nil;
}
return indexPath; }
How can I make the disabled cells "visible"?
This works, but I only on start up, because the indexPath of every cell changes while scrolling (reuse cells):
for(int i = 3; i <= 100; i++) {
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]] setAlpha:0.5];
}
You can disable the cells by setting the selectionStyle property, see the code below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
Another way to disable the cell selection is by disabling the user interaction of the cell. See the code below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...
cell.userInteractionEnabled = NO;
}
Enjoy. :)
It's best to set the opacity of the disabled cells in tableView:cellForRowAtIndexPath:. As #Parcs correctly says in their answer, you can also set userInteractionEnabled for the disabled cells to NO there.
You can just ignore the taps on inactive cells in tableView:didSelectRowAtIndexPath:.
Since the other answer providing code is outdated only showing how it's done in obj-c, here is a swift solution.
To disable a cell you need to set the isUserInteractionEnabled flag of a UITableViewCell to false.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// dequeue cell
...
cell.isUserInteractionEnabled = false
return cell
}
For subclasses of UITableViewCell you probably also want to alter the appearance of the now disabled cell. You can do so by overriding the property to run additional code when the flag gets set.
override var isUserInteractionEnabled: Bool {
didSet {
super.isUserInteractionEnabled = isUserInteractionEnabled
// additional code to change text colors for example
}
}
This is a simple question that I thought would have an easy-to-find answer but didn't. I want to select a cell in a collectionview. The main problem is that I can't attach a gesture recognizer to a prototype cell. I want to grab the text from a label on the cell that is touched. I use the name in a different function in my view.
Or a simpler question: Is there a tutorial on tap selection from a list of items?
You have the method collectionView:didSelectItemAtIndexPath: in the delegate. This should fire when you collect the cell and give you the correct indexPath for that particular cell.
Use this indexPath in combination with the collectionView's cellForItemAtIndexPath: to access a particular cell.
Example:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self manipulateCellAtIndexPath:indexPath];
}
-(void) manipulateCellAtIndexPath:(NSIndexPath*)indexPath {
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
// Now do what you want...
}
And, as long as I'm here. Swift-version:
override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
manipulateCellAtIndexPath(indexPath)
}
func manipulateCellAtIndexPath(indexPath: NSIndexPath) {
if let cell = collectionView?.cellForItemAtIndexPath(indexPath) {
// manipulate cell
}
}
I created a fresh project (Xcode 4, Master-Detail application) just to see if I'm doing something wrong, but I still have the same problem. I want to call -reloadData when the user deselects a cell, so this is my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(#"%s", __PRETTY_FUNCTION__);
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%s", __PRETTY_FUNCTION__);
[tableView reloadData];
}
-(NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%s", __PRETTY_FUNCTION__);
return indexPath;
}
The problem is that didDeselectRowAtIndexPath and willDeselectRowAtIndexPath don't seem to be called. Is this the expected behavior? The docs for tableView:didDeselectRowAtIndexPath: state
Tells the delegate that the specified row is now deselected.
so I guess that it should work as I thought.
If you call deselectRowAtIndexPath:animated:, the delegate methods tableView:willDeselectRowAtIndexPath: and tableView:didDeselectRowAtIndexPath: message are not sent.
the documentation of tableView:willDeselectRowAtIndexPath: also says that
This method is only called if there is an existing selection when the
user tries to select a different row. The delegate is sent this method
for the previously selected row. You can use
UITableViewCellSelectionStyleNone to disable the appearance of the
cell highlight on touch-down.
It not worked for we when I used UITableViewCellSelectionStyleNone.
One other quirk that I've found — in IOS 5.1, at any rate — is that if you call reloadData on the table, you won't get didDeselectRowAtIndexPath for any selected rows. In my case, I adjust the cell layout slightly depending on whether it's selected or not, so I needed to manually do that work prior to reloading the table data.
I know this is an old question but I just ran into the same problem.
If you use
[tableView reloadData]
Then The table data is reloaded and no rows are selected behind the scenes - meaning
only
didSelectRowAtIndexPath
is ever called. I hope this helps someone who comes across this problem.
A clean way of solving this problem is to do the following:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath)
// Do your cell setup work here...
//If your cell should be selected...
if cellShouldBeSelected {
tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
}
return cell
}
This solves this entire problem of a cell not responding to being deselected after a tableView.reloadData()call happens.
When any cell is selected the first time, the -[UITableViewDelegate tableView:didDeselectRowAtIndexPath:] method is not called, but the -[UITableViewDelegate tableView:didSelectRowAtIndexPath:]. Just after selecting one more cell, the didDeselectRowAtIndexPath is called right after the didSelectRowAtIndexPath.
This is OK.
But if you have to show a cell as selected at the begining, (e.q. using UITableViewCellAccessoryCheckmark), then, after selecting another cell you probably want the didDeselectRowAtIndexPath method being called the first time, to deselect the previous cell.
The solution!
You have to call the -[UITableView selectRowAtIndexPath:animated:scrollPosition:] in the -[UITableViewDataSource tableView:cellForRowAtIndexPath:] to notify that a wanted cell is already selected.
Objective-C
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// saving the current selected row
SelectedRow = indexPath.row;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
#pragma mark - UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
// preventing selection style
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = "Some text";
if (indexPath.row == SelectedRow) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// just wanted to make it selected, but it also can scroll to the selected position
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
return cell;
}
Swift 3.1
// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)!
cell.accessoryType = UITableViewCellAccessoryType.checkmark
selectedRow = indexPath.row
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)!
cell.accessoryType = UITableViewCellAccessoryType.none
}
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
// preventing selection style
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.textLabel?.text = "some text"
if (indexPath.row == selectedRow) {
cell.accessoryType = UITableViewCellAccessoryType.checkmark
// just wanted to make it selected, but it also can scroll to the selected position
tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableViewScrollPosition.none)
}
return cell
}
You just have to set the selection to be "Multiple Selection" as Xcode allow you to deselect the cells in this mode only.
Xcode Screenshot
Set allowsMultipleSelection for that tableview to TRUE
self.tableView.allowsMultipleSelection = YES;
For me it's started working by adding ->super.setSelected(selected, animated: animated)
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)//This was missing
}
I think it's just simple mistake!
Why don't you use following:
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
Instead of using just "tableView" in that line?
I guess, and pretty sure that above line would give you the solution! hope this helped you, if you looked back at your old question!!!
Kudos! :)
First of all, you have to set allowsMultipleSelection is true and set delegate by below code
self.tableView.allowsMultipleSelection = true
self.tableView.delegate = self
If you use tableView.reloadData() in didSelectRowAt delegate method, remove this.
in your custom cell, use selected method.
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
//write your code here for selection method
}
By this method, your cell state is selected. If you again click on selected cell, then didDeSelect delegate method will call automatically.
One source of this issue is setting isSelected directly on the table view cell, rather than telling the table view to select the cell via selectRow(at:animated:scrollPosition:) or deselect it via deselectRow(at:animated:).
The cell's isSelected property is not the source of truth that the table view uses to determine whether to call the delegate's didDeselect method (although a cell whose isSelected is set does seem to prevent the table view from calling the delegate's didSelect method—meaning that the cell can get in a state where it is impossible to select or deselect via the UI).