How to implement a "Favorites" section in my tableview

Discussion in 'iOS Programming' started by Thelle93, Jan 9, 2014.

  1. Thelle93 macrumors newbie

    Joined:
    Oct 7, 2012
    #1
    I wan't to implement a favorited section on my iOS app in my tableview. So if i click on a left detail image on a row, it should go up to a "Favorites" section and still be showing in the standard section. and the same by removing it in favorites. Like this:

    screen568x568.jpeg

    Please give me some hints! :)
    Thanks in advance
     
  2. waterskier2007 macrumors 68000

    waterskier2007

    Joined:
    Jun 19, 2007
    Location:
    Novi, MI
    #2
    I am not going to write it for you, but you could try having two arrays. One for favorites, one for regular items. Setup your table view controller to pull from a certain array based on the section property of the table view cell indexPath. If an item in your section # 2 is selected, add it to the favorites array and reload the data. You should be able to figure out the rest from there
     
  3. D.T. macrumors G3

    D.T.

    Joined:
    Sep 15, 2011
    Location:
    Vilano Beach, FL
    #3
    Basically you’re going to setup sections in your UITableView, there’s a ton of tutorials online, and a bunch of built-in methods to handle this (numberOfSectionsInTableView, titleForHeaderInSection). In short: you define the number of sections (2 in your case, though this is easily done dynamically as well), apply titles, then handle allocation of each data member to the section during the view generation (i.e., in cellForRowAtIndexPath).

    You’re going to want to have a construct to handle this, something like an array with arrays/dictionaries that are allocated to the proper key based on the section target. Then some basic use of didSelectRowAtIndexPath to determine the tap, assign (or deassign) the cell data to the favorites section, refresh your datasource.

    Should all be pretty standard UITV patterns, good luck :)
     
  4. Thelle93 thread starter macrumors newbie

    Joined:
    Oct 7, 2012
    #4
    Yes I have aldready Done that, i have one array with the standard rows and then another one that is empty. I have two sections with headers and the top one is empty because favorites array is empty. In the table view the original array is loaded into the cellidentifier.

    Should I have another cellidentifier for the favorited rows?
    And use the method moveRowAtIndexPath?
    This is where I'm stuck, moving the row to the first section when it gets tapped on the image.
    Thanks again:)
     
  5. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #5
    If you're already that far along, providing us some of your code might help.

    I would suggest: no, not unless those cells have a different layout.

    Based on this requirement from your first post, it doesn't sound like you want to move the row at all, but duplicate it into the Favorites section:
    What does your tableView:didSelectRowAtIndexPath: method look like?
     
  6. Thelle93 thread starter macrumors newbie

    Joined:
    Oct 7, 2012
    #6
    Yes you are I just wan't to duplicate them. okay here is some relevant code.

    Code:
    - 
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        
        static NSString *CellIdentifier = @"Cell";
        UITableViewCell *segueRow = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        segueRow.textLabel.text=self.objects[indexPath.row];
    
        //Conditionally perform segues, here is an example:
        if ([segueRow.textLabel.text isEqualToString:@"xxx"] )
        {
            [self performSegueWithIdentifier:@"xxx" sender:self];
        }
        else if .........
        }
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
    }
    
    (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
       
        
        static NSString *CellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if(cell)
            cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.textLabel.text=self.objects[indexPath.row];
        cell.detailTextLabel.text =self.subtitles[indexPath.row];
        cell.detailTextLabel.textColor=[UIColor lightGrayColor];
        
        
        // left image
        UIImageView *image=[[UIImageView alloc] initWithFrame:CGRectMake(7, 7, 30, 30)];
        [image.layer setCornerRadius:image.frame.size.width/2];
        [image setClipsToBounds:YES];
         image.image=[UIImage imageNamed:[self.icons objectAtIndex:indexPath.row]];
        [cell.contentView addSubview:image];
        
        //Favorites
        cell.imageView.userInteractionEnabled = YES;
        cell.imageView.tag = indexPath.row;
        UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(addToFavs:)];
        tapped.numberOfTapsRequired = 1;
        [cell.imageView addGestureRecognizer:tapped];
    
        return cell;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        if(section==0)return [self.favs count];
        else return [self.objects count]; 
    }
    
    -(void)addToFavs:(id)sender{
        UITapGestureRecognizer *gesture = (UITapGestureRecognizer *) sender;
        NSLog(@"Tag = %d", gesture.view.tag);   
    }
    
    "objects", "subtitles" and "icons" is the titles, subtitles and left image arrays. And "favs" is the empty array.

    As you see at he bottom I have an IBAction to get the tag of the tap, but it doesn't recognize it because the image is in a contentView... If I change it to recognize the contentView it works but then I can tap all over the cell and thats not how it's suppose to be.
     
  7. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #7
    So, the real concern is not "how can I write code to achieve this functionality" but rather "why doesn't the code I already wrote achieve this functionality". Ah. I would suggest trying to be as explicit as possible in your queries so that we better understand what you're asking. Maybe have a read through this excellent article from Mike Ash: Getting Answers.

    On to the actual issue: You seem to be adding a UIImageView to your contentView, but then add the gesture recognizer to the cell's built-in imageView. I doubt that's what you really want.

    Plus, you don't need to be doing these things every time cellForRowAtIndexPath: is called. I would suggest moving most of that code into the "if(cell)" block.
     
  8. Thelle93 thread starter macrumors newbie

    Joined:
    Oct 7, 2012
    #8
    Well I have different images to each row, and i'm modifying the image. So I have to add it in the contentView, right? I know that code is wrong, that's why I didn't provide it from the beginning. But I think I can fix that problem by my self.

    I just need to figure out how to duplicate the rows into section=0 when the imageView is tapped. Then when tapped again back to section=1. I know it's simple but I can't find anything related on google. Sorry if I'm unclear in this thread, I am a completely newbie in ios development.

    Thanks again.
     
  9. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #9
    No. The cell already has an imageView, as your UITapGestureRecognizer addition shows. Why add another one?

    Don't get ahead of yourself yet. Apply some basic debugging principles. I think you need to figure out why the gesture method is not getting triggered. Fix that first. Then you can worry about adding to the favorites.
     
  10. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #10
    No, you don't understand what Dojo is telling you.

    A cell gets created once, and then reused many times. The logic for cellForRowAtIndexPath should be:

    Code:
    cell = dequeue old cell.
    if (!cell)
    {
       cell = alloc a new cell
       add custom fields to cell
    }
    
    now fully configure all cell fields, including installing text and images

    The only time you should add fields to a cell is if you get back nil from the dequeue call, and need to create a new cell. If you DO get back a recycled cell, it will already have the fields you need.

    Note that you can also set up cell prototypes if you're using starboards. That's a cleaner, more modern way to do things.
     
  11. Thelle93, Jan 9, 2014
    Last edited: Jan 9, 2014

    Thelle93 thread starter macrumors newbie

    Joined:
    Oct 7, 2012
    #11
    Now it's working. I now have a "star" image as the accessoryView and the tags are working!

    Code:
    //fav image
            UIImageView *fav = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"favorites.png"]];
            cell.accessoryView = fav;
            [fav setFrame:CGRectMake(0, 0, 25, 25)];
            [fav setClipsToBounds:YES];
            if(!indexPath.section==0) {
                fav.image=[UIImage imageNamed:@"unfavorites"];
            }
            //Favorites
            cell.accessoryView.userInteractionEnabled = YES;
            cell.accessoryView.tag = indexPath.row;
            UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(addToFavs:)];
            tapped.numberOfTapsRequired = 1;
            [cell.accessoryView addGestureRecognizer:tapped];
    So now I can move on...
     
  12. Thelle93 thread starter macrumors newbie

    Joined:
    Oct 7, 2012
    #12
    I got it sort of working, but only the textLabel gets in to that section at the right row, both the subtitle and left-image doesn't get to the right index. So I guess I have to create two more arrays for the subtitle and left image too, and do the same with them?... Or is there a better way?

    Code:
    -(void)addToFavs:(id)sender{
        UITapGestureRecognizer *gesture = (UITapGestureRecognizer *) sender;
        NSLog(@"Tag = %d", gesture.view.tag);
        [self.favs addObject:[self.objects objectAtIndex:gesture.view.tag]];
        [self.tableView reloadData];
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        
        
        static NSString *CellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell){
            cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        }
        //cell.detailTextLabel.textColor=[UIColor lightGrayColor];
        cell.textLabel.text=self.objects[indexPath.row];
        cell.detailTextLabel.text =self.subtitles[indexPath.row];
        
        
        // left image
        UIImageView *image=[[UIImageView alloc] initWithFrame:CGRectMake(7, 7, 30, 30)];
        [image.layer setCornerRadius:image.frame.size.width/2];
        [image setClipsToBounds:YES];
        [image.layer setBorderColor:[UIColor lightGrayColor].CGColor];
        [image.layer setBorderWidth:0.3f];
        image.image=[UIImage imageNamed:[self.icons objectAtIndex:indexPath.row]]; 
        
        //fav image
        UIImageView *fav = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"favorites.png"]];
        cell.accessoryView = fav;
        [fav setFrame:CGRectMake(0, 0, 25, 25)];
        [fav setClipsToBounds:YES];
        if(!indexPath.section==0) {
            fav.image=[UIImage imageNamed:@"unfavorites"];
        }
        //Favorites
        cell.accessoryView.userInteractionEnabled = YES;
        cell.accessoryView.tag = indexPath.row;
        UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(addToFavs:)];
        tapped.numberOfTapsRequired = 1;
        [cell.accessoryView addGestureRecognizer:tapped];
        
        //favorites content
        if (indexPath.section == 0)
        {
            cell.textLabel.text=self.favs[indexPath.row];
            [cell.contentView addSubview:image];
            cell.detailTextLabel.text =self.subtitles[indexPath.row];
        }
        else if (indexPath.section == 1)
        {
            cell.textLabel.text=self.objects[indexPath.row];
            [cell.contentView addSubview:image];
        }
        
        return cell;
    }
    
    
     
  13. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #13
    I would suggest storing your data for the table either in an array of dictionaries or, better yet, create a custom class to hold the data for each row. Then, because you should reference instances via a pointer, you can simply add and remove those objects from your favorites array.

    Also, you still haven't moved a bunch of code into your "if (cell)" block. That can cause performance issues and doesn't take advantage of cell reuse.
     
  14. Thelle93 thread starter macrumors newbie

    Joined:
    Oct 7, 2012
    #14
    Okay that's seems complicated... I thought this task should be simple, to just copy the cells into the first section.:p I think I will do as i said in the previous post and save the array with NSUserDefaults. cause it works:) And then add some method to remove them from the array if I tap the image again.

    But one thing, when I'm tapping the image and the row is added to the section, it just pops up with no animation, how can I fix that?

    About the if (cell) block I just moved most of the code in there now:)

    Thanks again.
     
  15. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #15
    Frequently, a task you think is simple will turn out not to be. And although a quick-and-dirty solution may be tempting, in the long run, you're better off stepping back a bit and designing a better, more-scaleable, easier-to-debug solution. It'll take more time but, usually, it's worth it.

    Take advantage of a UITableView's animated reloading methods. I would suggest reading through the entire Table View Programming Guide for iOS, just so you know everything that it is capable of.
     
  16. Thelle93 thread starter macrumors newbie

    Joined:
    Oct 7, 2012
    #16
    Yeah I found this code:
    Code:
    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
    
    But the problem is that I'm adding a new string to the array, not really inserting a new row to the tableview, or am I wrong?
     
  17. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #17
    Try reloadSections:withRowAnimation: or reloadRowsAtIndexPaths:withRowAnimation:.
     

Share This Page