I am new to Objective C. I am having the UITableView in that I have to select the tableview cell from top to bottom also without selecting the first cell the second cell won't be selected and the user won't able to select the in-between cells also.can anyone help me how to solve this?
I added the example image here if the user selected the first cell the second cell want to enable and if the user selects the second cell the third cell want to enable remaining cells want to disable.
- (void)viewDidLoad {
[super viewDidLoad];
self.colors = [[NSArray alloc] initWithObjects: #"Red", #"Yellow", #"Green",
#"Blue", #"Purpole", nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.colors count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [self.colors objectAtIndex:indexPath.row];
return cell;
}
Your question is not clear, Hope you have an issue with making multiple cell selection:
Allow UITableView to select multiple cells by tableView.allowsMultipleSelection = true;. And then a user can select any number of cells in the table.
Using this way you can able to select the current row and enable next row for selecting. You need to handle another way to disable others row in cellForRowAtIndexPath method.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.checkMark.selected = YES;
NSString *cellText = cell.textLabel.text;
if(self.colors.count < indexPath.row+1 ){
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:0];
UITableViewCell *nextCell = [tableView cellForRowAtIndexPath:nextIndexPath];
nextCell.checkMark.userInteractionEnabled = YES;
//OR
nextCell.userInteractionEnabled = YES;
}
}
Related
I want UITableView always has the text "No more items" in the last cell, I want this row appears regardless of number of items, include 0 items. How could I do?
You can get the total row of a section by using numberOfRowsInSection: method.
NSInteger total = [self.tableView numberOfRowsInSection:indexPath.section];
// Check if last cell or no cell.
if(indexPath.row == totalRow -1 || indexPath.row == 0)
{
// Do your operation here
}
You can perform this in your cellForRowAtIndexPath or willDisplayCell methods. Hope it helps.
Just add the row regardless of your other items:
- (void)buildMenu {
[_menuItems removeAllObjects];
// ... add your items or not if you have 0
YourCellItem *lastItem = [[YourCellItem alloc] initWithTitle:#"No more items"];
[_menuItems addObject:lastItem];
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _menuItems.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"yourCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
YourTableViewCell *basicCell = (YourTableViewCell *)cell;
YourCellItem *item = [_menuItems objectAtIndex:indexPath.row];
basicCell.itemTitle.text = item.title;
return cell;
}
I have a UITableView in a UIViewController. It's Selection modes are set to Single and Multiple Selection During Editing.
When I set the tableView's editing property to YES, if a process asynchronously runs reloadData, any of my current check marks that have been selected disappear. I implemented my tableView:cellForRowAtIndexPath: method such that it sets the selected property of the cell, based on what the selection was before the reloadData. I've verified those are being set correctly. But to no avail, they still don't show up selected. What do I need to change so that it gets that set correctly? It seems to retain the indented editing mode just fine.
Here's said method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ValveCell";
ValveCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Valve *valve = self.sections[indexPath.section][indexPath.row];
cell.subject = valve;
cell.selected = [self.selections member: valve];
return cell;
}
I use the didSelect and didDeselect methods to keep selections (an NSMutableSet) up to date. E.g.
- (void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableArray *section = self.sections[indexPath.section];
[self.selections removeObject: section[indexPath.row]];
}
Clarification
Given the answers, I think I did not clarify which check marks I was referring to. I am referring to the blue check marks that show a selected state on the left side of the table when the table has had its editing set to YES as shown in this picture:
If a reloadData occurs, it will clear those blue check marks.
Update: My Current Solution
What I did to retain the visual selection status is to modify the KVO method that was firing the reloadData to include code to programmatically reselect the current selections:
...
[self.tableView reloadData];
if (self.tableView.editing) {
[self.sections enumerateObjectsUsingBlock:^(id valves, NSUInteger section, BOOL *stop) {
[(NSArray*)valves enumerateObjectsUsingBlock:^(id valve, NSUInteger row, BOOL *stop) {
if ([self.selections member: valve]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row inSection: section];
[self.tableView selectRowAtIndexPath: indexPath animated: NO scrollPosition: UITableViewScrollPositionNone];
}
}];
}];
}
...
The selectRowAtIndexPath:animated:scrollPosition: method does the work.
So at this point, I guess my question is, do I really have to do this? Or I'm working too hard?
Setting the cell to "selected" in cellForRowAtIndexPath won't force a call to the UITableView delegate methods so if that's where you're setting your checkmarks, they won't automatically appear upon the table view's reload.
You can either add the checkmarks directly in cellForRowAtIndexPath, ex:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ValveCell";
ValveCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Valve *valve = self.sections[indexPath.section][indexPath.row];
cell.subject = valve;
cell.selected = [self.selections member: valve];
if (cell.selected) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Or you can call selectRowAtIndexPath manually, ex:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ValveCell";
ValveCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Valve *valve = self.sections[indexPath.section][indexPath.row];
cell.subject = valve;
cell.selected = [self.selections member: valve];
if (cell.selected) {
[self tableView:tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
return cell;
}
I'm working on an iOS storyboard app with a UITableView and dynamic cells. I want a segmented control to determine which type of cell populates the tableView contents.
Here is The viewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
[_reelsOrAllSegmentedControl addTarget:self
action:#selector(segmentedControlTouched)
forControlEvents:UIControlEventValueChanged];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier0 = #"cellType1";
NSString *CellIdentifier1 = #"cellType2";
long row = [indexPath row];
if([_segmentedControl isEnabledForSegmentAtIndex:0])
{
CellType1 *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier0
forIndexPath:indexPath];
NSArray *folderArray = [[NSArray alloc] initWithArray:[[UserSession sharedSession] userFolders]];
[[cell titleTextView] setText:(NSString*)folderArray[row][#"title"]];
else if([_segmentedControl isEnabledForSegmentAtIndex:1]){
CellType2 *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier1
forIndexPath:indexPath];
NSArray *postArray = [[NSArray alloc] initWithArray:[[UserSession sharedSession] userPosts]];
cell.captionTextView.text = postArray[row][#"caption"];
return cell;
}
-(void)segmentedControlTouched
{
NSLog(#"touch");
[self.tableView reloadData];
}
Every time I switch the segmented control, the first block of the if is run. Shouldn't the second one be run when the table is refreshed?
You would be better off creating two separate classes implementing UITableViewDataSource, and setting the table view's dataSource appropriately when the user changes the selected segment.
But anyway, you can get your code working by looking at the control's selectedSegmentIndex property:
if(_segmentedControl.selectedSegmentIndex == 0) {
CellType1 *cell = [tableView
... etc.
I have a UIViewController with a UITableView which has multiple accessory checkmark implemented into it. My problem is, when I click some cells in the tableview it gets checked but there will be some other cell also checked below. I can view it when I scroll down the tableview. I would only want the cell to be checked whichever the user is clicking and not the extra cells. Please let me know how can I do it. Thanks.
Here is the code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [someData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text=[self.someData objectAtIndex:indexPath.row];
cell.textLabel.font=[UIFont fontWithName:#"Times New Roman" size:11];
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
[cell.textLabel setNumberOfLines:2];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
if ([selectedCell accessoryType] == UITableViewCellAccessoryNone) {
[selectedCell setAccessoryType:UITableViewCellAccessoryCheckmark];
NSArray *arrayOfIndexPathsTableOne = [self.tableView indexPathsForSelectedRows];
for (int i = 0; i < [arrayOfIndexPathsTableOne count]; i++) {
NSIndexPath *indexPathImInterestedIn = [arrayOfIndexPathsTableOne objectAtIndex:i];
UITableViewCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPathImInterestedIn];
[saveData addObject:[NSString stringWithFormat:#"%#", currentCell.textLabel.text]];
}
} else {
[selectedCell setAccessoryType:UITableViewCellAccessoryNone];
NSArray *arrayOfIndexPathsTableOne = [self.tableView indexPathsForSelectedRows];
for(int i = 0; i < [arrayOfIndexPathsTableOne count]; i++) {
NSIndexPath *indexPathImInterestedIn = [arrayOfIndexPathsTableOne objectAtIndex:i];
UITableViewCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPathImInterestedIn];
[saveData removeObject:[NSString stringWithFormat:#"%#", currentCell.textLabel.text]];
}
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
This is a cell reuse problem.
To fix, ensure that each time you return a cell from cellForRowAtIndexPath: you always set the selection (checkmark) status. That means explicitly setting it true and false as appropriate.
There are only 2 things you need:
Disable multiple selection on your UITableView.
Implement setSelected:(BOOL)selected in your UITableViewCells and make it select/deselect the cell's sub-views (your checkmarks) accordingly.
When you select table you show check box on it but when you scroll and the cell desapeare and it goes to reusable pool after you scroll back the cell is taken from this pool and it doesn't remember the 'state'.
The solution is create NSMutableArray and in didSelectCellAtIndexPath: method add or remove the indexPath for that cell to the array and in cellForRowAtIndexPath: check this array and show/hide checkmark base on that table.
dont write this line in cell for row at index path
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
it must be in loadview or viewdidload
i have a uitableview with custom cells.. with normal code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
DDMainCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[DDMainCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
}
the problem is when i select one cell i add progress bar on the cell that download data online.. but when i Scroll down i find that every 10 cells have the same progress bar .. how can i prevent this behavior ?
Try this,
- (void)viewDidLoad
{
[super viewDidLoad];
dataarr=[[NSMutableArray alloc]init];
indexarr=[[NSMutableArray alloc]init];
mytableview=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
mytableview.dataSource=self;
mytableview.delegate=self;
[self.view addSubview:mytableview];
for (int i=0; i<30; i++) {
[dataarr addObject:[NSString stringWithFormat:#"%d",i]];
}
// Do any additional setup after loading the view, typically from a nib.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [dataarr count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] ;
}
cell.textLabel.text=[dataarr objectAtIndex:indexPath.row];
UIActivityIndicatorView *act=[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[act setFrame:CGRectMake(50, 20, 20, 20)];
act.hidden=YES;
[cell.contentView addSubview:act];
cell.selectionStyle=UITableViewCellSelectionStyleNone;
if ([indexarr containsObject:[dataarr objectAtIndex:indexPath.row]])
{
[act startAnimating];
act.hidden=NO;
return cell;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexarr containsObject:[dataarr objectAtIndex:indexPath.row]])
{
[mytableview reloadData];
return;
}
[indexarr addObject:[dataarr objectAtIndex:indexPath.row]];
[mytableview reloadData];
}
Make sure, when downloading is complete, then remove this object from indexarr....
That's because your cells are getting reused; UITableView will put off-screen cells into the reusable cell queue, and dequeue them for reuse if the reuseIdentifier matches. You should use some other data structure (e.g. NSArray or NSDictionary) to track which indices/cells have already been tapped. Then, in the method you showed above, regardless of whether the cell was init-ed or dequeued, set the progress bar according to your underlying data structure.
Here your used UITableViewCellIdentifier is reuseIdentifier. Which will work for all Cells are same type. Now your are taking once cell with progress bar. Now it will different from all cells data.
So use one more tableview cell for progress bar, or while reloading table remove the Progress bar which is exists already and add again. for this use tag for progress bar.