I have implemented the SWRevealController (a slide out menu), I referecned AppCoda.com for som help, here's the link (http://www.appcoda.com/ios-programming-sidebar-navigation-menu/).
I have succesfully implemented it, using the same way as AppCoda explains, setting cellIdentifiers for each cell in the sidebar and then styling them in the storyBoard. It all works great! However...
THE PROBLEM
So the problem is that I want sections in this table view, so that I can have different cells in different sections, but using the code I have now (which is below), I just get a repeated cell (the same cells as in my first section). I am having trouble with the sections because styling each cell based on their cell identifier.
This is what I get...
SO MY RESOLUTION (THAT DIDN'T WORK)
So what I tried to do was create multiple arrays, one for each section, and styled them like this,
if (indexPath.section == 1) {
cell.textLabel.text = [_fitnessItems objectAtIndex:indexPath.row];
if (indexPath.row == 0) {
cell.imageView.image = [UIImage imageNamed:#"torso-25.png"];
}
if (indexPath.row == 1) {
cell.imageView.image = [UIImage imageNamed:#"weightlift-25.png"];
}
if (indexPath.row == 2) {
cell.imageView.image = [UIImage imageNamed:#"barbell-25.png"];
}
}
But it worked, and I got what I wanted, however, it was very gilitchy when I tried to do an action when a cell was pressed, I was doing that like this...
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{
[self performSegueWithIdentifier:#"test" sender:nil];
}
Now there might do be a way to fix this issue or fix the cell identifiers. Anyways that's what I tried doing.
WHAT I AM DOING NOW
So if you don't quite understand what I am doing, check out that AppCoda tutorial cause thats what I did. But I want sections between some cells. What I tried doing is this and it dosen;t work...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 1) {
NSString *CellIdentifier = [self.fitnessItems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
else {
NSString *CellIdentifier = [self.menuItems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
So I have two arrays of cell Identifiers and I try to allocate them to each section, but it dosen't work?
Any help would be greatly appreciated, and thanks in advance.
Related
I am a beginner in iOS development. I face a problem where I need little help.
I wrote a small program to learn custom UITableViewCell. It just works well at beginning; after that, when I slide the view, it changes size of the cell. I am confused about where I might be going wrong. The ContentView have only one view which is UIImageView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
HWHomePageCell *cell = [tableView dequeueReusableCellWithIdentifier:#"HWHomePageCell"];
if (cell == nil)
{
cell = [HWHomePageCell homePageCell];
}
cell.imageView.image = [UIImage imageNamed:#"XD"];
return cell;
}
These are the pictures. Top two are good, but when I slide down, as you can see, the 3rd doesn't work well
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
HWHomePageCell *cell = [tableView dequeueReusableCellWithIdentifier:#"HWHomePageCell"];
cell = [HWHomePageCell homePageCell];
cell.imageView.image = [UIImage imageNamed:#"XD"];
return cell;
}
When you use custom cell then no need to check whether cell is nil or not. so please remove this condition and implement it.
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
This is my code, which shows a problem for 14 rows of a table view. For one screen, 6 cells are visible.
When I tapped the 2nd cell, the 10 cell also has a checkmark, tap 3rd then 11th checkmarked, 1st then 9th checkmarked, tap 1st, then 8th also checkmarked...but the grey highlight does not behave like this, only one cell can be highlighted.
If I tap a cell after one is tapped on the the same screen, the one just checkmarked will be cleared for checkmark, which makes sense. However, if after I tapped one cell and then scroll the tableview lower, I can tap a cell and it shows checkmark as well, i.e. the one just checked on top screen still has a checkmark. So if I scroll up and down, and tap one cell each time after I scroll to the other side, I can put a checkmark for every cell, all checkmarks are shown.
It is really weird, and I have tried a lot of ways to solve it, but seems I have some basic understanding of UITableView missing, can anyone figure it out, please? Thanks.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID= #"UITableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
cell.imageView.image = [UIImage imageNamed:#"earth.png"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =[tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =[tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
You're not storing the existence/absence of your checkmark in any sort of persisted data source and showing/hiding it in cellForRowAtIndexPath. Because of this, your table is reusing your cells, and it just throws it up as it found it. So if it reuses a cell that had your checkmark accessory enabled, it shows it as enabled (because you didn't specifically disable it in cellForRow)
I would keep an NSMutableArray of selected indexPath objects. Add the indexPath in didSelectRowAtIndexPath, and remove it in didDeselectRowAtIndexPath. Then, in cellForRowAtIndexPath, check if the current indexPath exists in the array and enable the checkmark if so, disable if not.
Stonz2's answer is right.
For example, you should create an object for table view data source and it should has a property to show is it checkmarked.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
...
Item *item = [self.itemArray objectAtIndex:indexPath.row];
if (item.checkmarked) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
Item *tappedItem = [self.itemArray objectAtIndex:indexPath.row];
tappedItem.checkmarked = !tappedItem.checkmarked;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
All I'm looking to do is get the selected (checkmarked) rows from my UITableView and show them in my console log. Doesn't seem like it should be so difficult. I've found two methods that I'll display below. Neither work despite the logic mostly making sense to me. Which would you suggest and how can I tweak to make it work?
My TableView Code:
I don't think this is completely necessary to the issue, but I know it sometimes helps to see the whole picture.
#pragma mark - tableView datasource
- (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 [self.places count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *tempDictionary= [self.places objectAtIndex:indexPath.row];
cell.textLabel.text = [tempDictionary objectForKey:#"name"];
if([tempDictionary objectForKey:#"vicinity"] != NULL)
{
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#",[tempDictionary objectForKey:#"vicinity"]];
}
else
{
cell.detailTextLabel.text = [NSString stringWithFormat:#"Address Not Available"];
}
return cell;
}
//Handles tableView row selection and addition and removal of the checkmark
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:newIndexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
}
}
Method 1:
Add a conditional statement to the end of the checkmark handler to add/remove selections to and from an array. Then create a button action that simply calls the array and displays it in the console. I think this is clunky but could work.
//Handles tableView row selection and addition and removal of the checkmark
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
//Reflect deselection in data model
}
if ([[theTableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark) {
[_selectedCellIndexes addObject:indexPath];
}
}
- (IBAction)sendResults:(id)sender {
NSLog(#"Add these: %#", _selectedCellIndexes);
}
Method 2:
Get the selected rows AND send to console log only when button is tapped. This seems to be the more logical method, but I can't seem to get it to work either. It doesn't throw any errors, but returns "Selected Items: (null)" in the console. What have I missed?
//Sends checkmarked items to console log
- (IBAction)sendResultsOption1:(id)sender {
NSMutableArray *aList = [[NSMutableArray alloc] init];
for (NSIndexPath *indexPath in _tableView.indexPathsForSelectedRows) {
NSString *r = [NSString stringWithFormat:#"%li",(long)indexPath.row];
[aList addObject:r];
}
NSLog(#"Selected Items: %#", _aList);
}
For what it's worth, I've also followed the instructions here without any luck. Hope you guys can help. Thanks in advance!
In Method 1, your method looks like this:
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
But you're referring to indexPath in the body. You don't have an indexPath (the undeclared identifier), but you have a newIndexPath, so at a minimum, this is the start of your problems and should be fixed first.
Giving your variables the right names looks like it should work for this approach...
In Method 2, the problem is none of your table view cells are selected. In you didSelectRowAtIndexPath method, you check the accessory icon to a check mark, then you deselect the row. So there are no objects in _tableView.indexPathsForSelectedRows.
In this approach, you need to change your for loop. Instead you need to iterate through every index path, and check on the accessory icon. If it's a check mark, add it to the array. Now log the array.
As far as which approach would be preferable, it depends on how you intend to use this ultimately. Obviously, the end goal isn't to NSLog the checkmarked rows--this is an iOS app we're talking about.
I have one UIViewController with UITableView inside,above table I have UISegmentControl, when I press on segment control I want to load a UItableCustomeCell, would you please help me in this implementation, I don't know how should I add them in cellForRowAtIndexPath, Since I have 3 different Custom cell
Here is the code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
if (indexPath.row == self.segmentedControl.selectedSegmentIndex == Test1) {
MytestsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MytestsCell"];
if (!cell) {
cell = [[MyBooksCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"MytestsCell"];
}
return cell;
}
else if (indexPath.row == self.segmentedControl.selectedSegmentIndex == tests) {
testCell *cell = [tableView dequeueReusableCellWithIdentifier:#"testCell"];
if (!cell) {
cell = [[TestsCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"testsCell"];
}
return cell;
}
break;
case 1:
if (indexPath.row == self.segmentedControl.selectedSegmentIndex == PTest) {
PTestsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PTestsCell"];
if (!cell) {
cell = [[PTestsCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"PTestsCell"];
}
return cell;
}
break;
}
I don't want to have 3 of them in one table, each custom cell is for one segment control
Thanks in advance!
One alternative I can think of is to switch the table views data source. But I would not recommend that. You could define a delegate of your data source and ask it for the table view cell for a selected segmented control. But this just moves the problem. I would stick to your approach.
So...here is what I would do. Starting with iOS6, you no longer need to check if your cell is nil after dequeuing from the tableview if you use
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath
You are guaranteed to get a cell back as long as the identifier exists. Also, it doesn't look like you need to do any additional configuration so something like this should work:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [NSString stringWithFormat:#"%d", self.segmentedControl.selectedSegmentIndex];
return [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
}
Edit: I forgot to add, that in order to use this, use numbers that correspond to the segments as the identifier for each cell.
I have a text array backing a TableView in iOS. In the cellForRowAtIndexPath: method I return a UITableViewCell* which is populated with text from the backing array. indexPath is used as the index into the backing array.
I now want to add a "Done" button to the last cell in the TableView. In my StoryBoard I've created a second (prototype) TableView Cell and gave it the identifier "ButtonCell". I've also added an extra element to the end of the backing array so numberOfRowsInSection: can return the count of the backing array and everything will just work.
I thought I would set the text of the last array element to something like #"donebutton" and then I could check for that in cellForRowAtIndexPath:. If it comes up true, I would know I'm at the end of my array and to return the "ButtonCell" cell instead of the normal "Cell". Thing is, it's not quite working right. What's the best way to accomplish this? Code snip is below.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
static NSString *ButtonCellIdentifier = #"ButtonCell";
UITableViewCell *bcell = [tableView dequeueReusableCellWithIdentifier:ButtonCellIdentifier forIndexPath:indexPath];
NSString *rowtext = [_mArCellData objectAtIndex:indexPath.row];
// return button cell if last item in list
if ([rowtext isEqualToString:[NSString stringWithFormat:#"%d", SUBMIT_BUTTON]])
{
NSLog(#"hit last row, so using button row");
return bcell;
}
cell.textLabel.text = rowtext;
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
static NSString *ButtonCellIdentifier = #"ButtonCell";
UITableViewCell *cell;
if (indexPath.row != ([_mArCellData count] - 1) { // if not the last row
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// configure cell...
} else { // last row
cell = [tableView dequeueReusableCellWithIdentifier:ButtonCell];
// configure button cell...
}
return cell;
}
I would just change your if statement to:
if ([tableView numberOfRowsInSection:0] == indexPath.row + 1) {
NSLog(#"hit last row, so using button row");
bcell.textLabel.text = rowtext;
return bcell;
}
This is a little more abstracted than your solution and doesn't rely on a property of a cell being set to anything in particular. I like the way #bilobatum put the dequeueReusableCellWithIdentifier: call in the if statement. That should save some memory as well.
EDIT: I also noticed that you are setting cell text, but not bcell text.