Hide a custom UITableViewCell from another UITableViewCell class - ios

I have two cells, let's say cell A and cell B in my app. Both of them have their own class, class CellA : UITableViewCell and class CellB : UITableViewCell.
I wish to hide cell B when user tap on a button in cell A.
How can I achieve this? Thanks.

if you want to hide cell B when tapping on a button in cell A (not the cell A itself), I think the good way is just to post a notification when clicking the button, let the UIViewController know it,then remove the datasouce of cellB and reloadData.

If you are using Static tableview content, just create two outlet of cell, lets say Cell1 and Cell2 with respective class of A and B with action methods actn_methodCell1 and actn_methodCell2
-(void)actn_methodCell1{ //action method for button in cell1
Cell2.hidden = YES;
[self.tableview reloadData];
}
-(void)actn_methodCell2{ //action method for button in cell2
Cell1.hidden = YES;
[self.tableview reloadData];
}
OR If you are using prototype tableview then, code this in didSelectCell
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
UITableViewCell *cell = [self.tableview cellForRowAtIndexPath:indexPath.row];
if([cell isKindOfClass:[A class]]){
for (UITableViewCell *cell in self.tableview) {
if([cell isKindOfClass:[B class]]){
cell.hidden = YES;
}
}
}
else if ([cell isKindOfClass:[B class]]){
for (UITableViewCell *cell in self.tableview) {
if([cell isKindOfClass:[A class]]){
cell.hidden = YES;
}
}
}
[self.tableview reloadData];
}

Related

reloadRowsAtIndexPaths cannot reload index path as expected

I add a image as avatar at indexPath [0,0], and use detailLabel display nickname at indexPath [0,1].
When use reloadRowsAtIndexPaths indexPath the indexPath [0,1] get a image...
I find when invoke reloadRowsAtIndexPaths the dequeue cell return nil.
Maybe the [0,0] cell be reuse at [0,1], I don't know why so that.
The code is:
- (void)viewDidLoad {
[super viewDidLoad];
_titles = #[#"Avator", #"Nickname"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDelayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_avator = [UIImage imageNamed:#"cat"];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDelayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_nickName = #"Smallfly";
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:1 inSection:0];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
});
});
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _titles.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *const cellIdenfier = #"cellIdentifier";
// Why reloadRowsAtIndexPaths [0,0] returned cell is nil?
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdenfier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdenfier];
}
if (indexPath.row == 0) {
[self configureCell:cell indexPath:indexPath];
} else {
NSString *nickName = _nickName ?: #"nickname";
cell.detailTextLabel.text = nickName;
}
cell.textLabel.text = _titles[indexPath.row];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath {
for (UIView *sv in cell.contentView.subviews) {
if ([sv isKindOfClass:[UIImageView class]]) {
[sv removeFromSuperview];
}
}
UIImage *avator = _avator ?: [UIImage imageNamed:#"user_profile_avatar"];
UIImageView *avatorImageView = [[UIImageView alloc] initWithImage:avator];
...
[cell.contentView addSubview:avatorImageView];
}
reloadRowsAtIndexPaths is a void method.
dequeueReusableCell always returns a cell (remove UITableViewCell alloc)
Objective-C
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
withRowAnimation:(UITableViewRowAnimation)animation
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"reuseIdentifier"
forIndexPath:indexPath];
// Configure the cell...
return cell;
}
Swift 3
open func reloadRows(at indexPaths: [IndexPath],
with animation: UITableViewRowAnimation)
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell =
tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier",
for: indexPath)
// Configure the cell...
return cell
}
You may want to look at a more recent tutorial or sample code.

Removing UITableViewCell Selection

Currently I'm overriding the standard UITableViewSelectionStyle by using UITableViewSelectionStyleNone and then changing the color the cell based on delegate methods:
- (void)tableView:(UITableView *)tableView
didHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setBackgroundColor:[UIColor yellowColor]];
}
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setBackgroundColor:[UIColor whiteColor]];
}
- (void)tableView:(UITableView *)tableView
didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"indexpath: %i",indexPath.row);
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setBackgroundColor:[UIColor whiteColor]];
}
- (void)tableView:(UITableView *)tableView
didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setBackgroundColor:[UIColor whiteColor]];
}
This almost works except that whenever I highlight a cell and then drag my finger off of it without actually selecting it, the color doesn't change to white...if I set it to [UIColor RedColor] it works perfeclty. Why is this...
Edit:
Somehow when I print out the indexPath.row after didUnhlightRowAtIndexPath I get "indexpath: 2147483647" from my NSLog
You could try:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
if you just want the highlight to go away after selecting a cell. Unless I misunderstand your question.
You can also try this
tableView.allowsSelection = NO;
another way of doing this
cell.selectionStyle = UITableViewCellSelectionStyleNone;
one more
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
You can also do it from the storyboard. Select the tableViewCell and under Attributes Inspector you can choose Selection > None
Here is a Swift 2 version
if let indexPaths = self.tableView.indexPathsForSelectedRows {
for indexPath in indexPaths {
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
Unselect selected row(s). You do not even need to know which one it is.
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
By maintaing a local instance of my indexPath, I was able to find the last selected cell and change its color to whitecolor. Seems really annoying that I have to maintain state myself, but it is what it is...
How about doing this with an extension?
import UIKit
extension UITableView {
func removeRowSelections() {
self.indexPathsForSelectedRows?.forEach {
self.deselectRow(at: $0, animated: true)
}
}
}
Usage:
tableView.removeRowSelections()
Simplest solution that worked for me (Swift 4.2)
(if you still want table view's row to be selectable)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let index = self.tableView.indexPathForSelectedRow{
self.tableView.deselectRow(at: index, animated: true)
}
}

How can I get the class (custom) of cell selected in UITableView?

Usually I get my selected cell this way:
- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = (CustomCell*) [table cellForRowAtIndexPath:indexPath];
}
But in the code I'm working with, I may have many kind of cells in my table view. How can I get the class of my selected cell (if it's for example CustomCell or CustomCell2) ?
You can check the type of cell returned
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[CustomCell class]]) {
//do specific code
}else if([cell isKindOfClass:[CustomCell2 class]]){
//Another custom cell
}else{
//General cell
}
SWIFT 4
Just in case, if someone needed it. Get the instance of selected cell and then check it for required tableViewCell type.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = myCustomCell.cellForRow(at: indexPath)
else
{
return
}
/** MyCustomCell is your tableViewCell class for which you want to check. **/
if cell.isKind(of: MyCustomCell.self)
{
/** Do your stuff here **/
}
}

How can I get UITableViewCell object from a UIButton action method?

I have a UITableView in which I have added a UIButton as accessory view for each cell. Note that I set the tag of the button as current row for future use.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cellButton = [UIButton buttonWithType:UIButtonTypeCustom];
cellButton.frame = CGRectMake(0, 0, 30, 30);
[cellButton setBackgroundImage:[UIImage imageNamed:#"cellButton.png"] forState:UIControlStateNormal];
[cellButton addTarget:self action:#selector(cellButtonAction:) forControlEvents:UIControlEventTouchUpInside];
cellButton.tag = indexPath.row; // <= Will use this in the next method
cell.accessoryView = cellButton;
}
//Load cell with row based data
return cell;
}
Now when one of these buttons is tapped, I need to make changes to the cell. So I implement cellButtonAction where I use the tag to get back the cell:
-(void)editCommentButtonAction:(id)sender
{
UIButton *button = sender;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag inSection:0];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[self makeChangesToCell:cell];
}
But this seems like a very round about way. Is there a cleaner way to do this?
So assuming that the button is in the contentView directly:
ask "sender" (ie the button) for its superview, which is the cell's contentView
ask that view for its superView, which is the cell
ask the tabview for the index of this cell:
- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell
EDIT: Actually, I use a general purpose method or function now that just walks up the superviews, looking for a view that is 'KindOf' a UITableViewCell or a UICollectionViewCell. Works like a champ!
The code in Swift:
func containingUITableViewCell(tableView: UITableView, var view: UIView) -> (UITableViewCell, NSIndexPath)? {
while let v = view.superview {
view = v
if view.isKindOfClass(UITableViewCell.self) {
if let cell = view as? UITableViewCell, let indexPath = tableView.indexPathForCell(cell) {
return (cell, indexPath)
} else {
return nil
}
}
}
return nil
}
func containingUICollectionViewCell(collectionView: UICollectionView, var view: UIView) -> (UICollectionViewCell, NSIndexPath)? {
while let v = view.superview {
view = v
if view.isKindOfClass(UICollectionViewCell.self) {
if let cell = view as? UICollectionViewCell, let indexPath = collectionView.indexPathForCell(cell) {
return (cell, indexPath)
} else {
return nil
}
}
}
return nil
}
You can do it in a easier way. You will get the table view cell using the sender parameter.
Check the following code.
-(void)editCommentButtonAction:(id)sender
{
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell*)[button superview];
[self makeChangesToCell:cell];
}
Here,
You are casting the sender of type id to a UIButton
You are calling the getter superview of that button, it will give you the UITableViewCell
Doing your customization.
You can get the cell as follows.
-(void)editCommentButtonAction:(id)sender
{
NSIndexPath* indexPath = 0;
//Convert the bounds origin (0,0) to the tableView coordinate system
CGPoint localPoint = [self.tableView convertPoint:CGPointZero fromView:sender];
//Use the point to get the indexPath from the tableView itself.
indexPath = [self.tableView indexPathForRowAtPoint:localPoint];
//Here is the indexPath
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[self makeChangesToCell:cell];
}
I had the same situation, the thing is that I had an imageView inside my tablecells, and I want to get the cell that holds the imageview that I tapped..
//MyCell is subclass of UITableViewCell
if ([[[[sender view] superview] superview] isKindOfClass:[MyCell class]]) {
MyCell *cell = (MyCell *)[[[sender view] superview] superview];
NSIndexPath *cellIndexPath = [myTable indexPathForCell:cell];
NSLog(#"cellIndexPath: %# - %#",cellIndexPath, [videoURLArray objectAtIndex:cellIndexPath.row]);
}
[sender view] - imageView
[[sender view] superview] -- where my imageView was placed (in this case, the superview of my imageView is the cell's contentView)
[[[sender view] superview] superview] --- where the contentView was placed -- the cell
The NSLog part should print the correct row and section of the tapped imageView.. Just Modify the code. ;)
Hello gigahari Use the following code.
- (void)cellStopDownloadButtonClicked:(id)sender
{
id viewType = [sender superview];
do {
viewType = [viewType superview];
}
while (![viewType isKindOfClass:[CustomCell class]] || !viewType);
CustomCell *cell = (CustomCell *)viewType;
// use the cell
}
This will work in all cases like ios 6 and ios 7. in ios 7 an extra view added in cell (content view).
if you use [[sender superview] superview] it will fail in some cases.
The way i usually do this:
cell stores the data for the given row or the index path
create a protocol with a method -didSelectSomething or -didSelectAtIndexPath:
the cell holds a reference to an instance of the protocol, which will be your datasource
wire the button action to the cell in your nib
have the cell call the delegate
DON'T FORGET to clean up the cell in prepareForReuse. Storing state in cells can lead to nasty bugs, so be sure to clean up on reuse.
The tag thing is a real hack, and it won't work if you have more than one section.
The sender superview will break if you reorder the views in your nib.
For this particular case (accessory view), isn't there a dedicated table delegate method?

How to remove the check mark on another click?

I want to make a table that user can select and deselect with a check mark:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath
{
...;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
- (void)tableView:(UITableView *) tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
...;
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
...;
}
I was trying to remove the check mark when clicking on the check-marked cell again, but it takes 2 clicks to do that instead of one.
If I set selection style to default, when I click on a selected row, it removes the blue highlight; clicking again, it removes the check mark.
I also tried some conditional statements in didSelectRowAtIndexPath, but they only respond to second click as well.
What causes the problem and how do I fix it?
You can try this one:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger index = [[tableView indexPathsForVisibleRows] indexOfObject:indexPath];
if (index != NSNotFound) {
UITableViewCell *cell = [[tableView visibleCells] objectAtIndex:index];
if ([cell accessoryType] == UITableViewCellAccessoryNone) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
} else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
}
}
This should toggle the cell's checkmark on every touch.
If you additionally want only one cell to appear selected at a time, also add the following:
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger index = [[tableView indexPathsForVisibleRows] indexOfObject:indexPath];
if (index != NSNotFound) {
UITableViewCell *cell = [[tableView visibleCells] objectAtIndex:index];
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
}
If you don't want the blue hilight background, simply set the cell's selection style to UITableViewCellSelectionStyleNone once you create the cell.
Based on Starter's link to Apple docs (my code in Swift 3):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: selectedIndexPath) {
cell.accessoryType = .none
}
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
selectedIndexPath = indexPath
}
The main point is to keep track of currently selected cell and change cell.accessoryType accordingly. Also don't forget to properly set cell.accessoryType in tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) based on selectedIndexPath.
Check this Apple official doc
best solution ever
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (index != NSNotFound) {
UITableViewCell *cell = [[tableView visibleCells] objectAtIndex:index];
if (cell.accessoryType == UITableViewCellAccessoryNone)
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else
cell.accessoryType = UITableViewCellAccessoryNone;
[self.tableName reloadData];
}
}
This helps in toggling the checkmarks (remove the check mark when clicking on the check-marked cell again)
You can use this:
You can use this:
if tableView.cellForRow(at: indexPath)?.accessoryType == .checkmark { tableView.cellForRow(at: indexPath)?.accessoryType = .none } else { tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark } tableView.deselectRow(at: indexPath, animated: true)

Resources