Bronze, Silver and Gold challenge solutions


#1

I saw there were no solutions posted for these challenges, so I thought I’d give it a go. Everything here is from ItemsViewController.m.

Bronze:

-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { return @"Remove"; }

Silver (Adding “No more items!” at the bottom):

[code]-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[[BNRItemStore sharedStore] allItems] count] + 1;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@“UITableViewCell”];

if(!cell) {
	cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"];
}

if([indexPath row] < [[[BNRItemStore sharedStore] allItems] count]) {
	BNRItem *p = [[[BNRItemStore sharedStore] allItems] objectAtIndex:[indexPath row]];
	[[cell textLabel] setText:[p description]];
}
else {
	[[cell textLabel] setText:@"No more items!"];
}

return cell;

}[/code]

Silver (Make “No more items!” immobile): This actually has the added bonus of preventing a user from trying to delete this row as well.

-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { if([[[BNRItemStore sharedStore] allItems] count] != [indexPath row]) { return YES; } else { return NO; } }

Gold:

-(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath { if([[[BNRItemStore sharedStore] allItems] count] != [proposedDestinationIndexPath row]) { return proposedDestinationIndexPath; } else { return sourceIndexPath; } }

This is actually my second try at getting through this book, even though I’ve purchased all three editions. Hopefully this time around I’ll make it all the way to the end with a good understanding of the subject.


#2

I had a very similar solution, with a minor difference to the resultant behavior to a user attempting to move a row below the “No more items!” row; rather than the moved row snapping back to it’s original location, it slides into the row just above the “No more items!” row.

- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath { if ([proposedDestinationIndexPath row] < [[[BNRItemStore sharedStore] allItems] count]) { return proposedDestinationIndexPath; } else { NSIndexPath *revisedIndexPath = [NSIndexPath indexPathForRow:([proposedDestinationIndexPath row] - 1) inSection:[proposedDestinationIndexPath section]]; return revisedIndexPath; } }


#3

Thanks for Sharing, its work with me,

is this book your first book in iOS programming? or you had some experience on objective-c?

in all challenges I know what I should to do, but I couldn’t figure out How or what the method I should use and implement !! even if I search in API and documentation I couldn’t !! :frowning:

every time I saw the solutions on this forum, I disappoint why I am not able to answer the challenges like them ?!! is it because of me or because of Book or because the they have background about iOS programming !


#4

[quote=“sl0o0m”]Thanks for Sharing, its work with me,

is this book your first book in iOS programming? or you had some experience on objective-c?

in all challenges I know what I should to do, but I couldn’t figure out How or what the method I should use and implement !! even if I search in API and documentation I couldn’t !! :frowning:

every time I saw the solutions on this forum, I disappoint why I am not able to answer the challenges like them ?!! is it because of me or because of Book or because the they have background about iOS programming ![/quote]

Yeah, this is my first iOS book, but I’ve semi-read the 2nd edition before. Gave it up for about a year. Raced through a Obj-C book prior to reading this edition, just to brush up on OO.

I had the same difficulty, until I changed my way of thinking. Searching the docs wasn’t helping me until after I did an initial search in google in less precise terms. I.e. “ios uitableview static row” or something along those lines. That would usually get me some pointers from sites like Stackoverflow, which I then took with me into the docs and gave me the answer to how I should do things. And a lot of trial and error. A. lot. One of the challenges in the book I had to keep trying to figure out over the span of three days, each day trying different things for a couple of hours until I finally got it. That experience alone taught me very much.

Keep at it, trial and error, google, docs; those are things you learn from. This book is - as the authors say - not in-depth in any areas, so it is your responsibility to delve deeper. I’ve already expanded a few of the apps we develop over the course of the book, just because you learn a lot that way. Hang in there :slight_smile:


#5

[quote=“TheEskil”]

Yeah, this is my first iOS book, but I’ve semi-read the 2nd edition before. Gave it up for about a year. Raced through a Obj-C book prior to reading this edition, just to brush up on OO.

I had the same difficulty, until I changed my way of thinking. Searching the docs wasn’t helping me until after I did an initial search in google in less precise terms. I.e. “ios uitableview static row” or something along those lines. That would usually get me some pointers from sites like Stackoverflow, which I then took with me into the docs and gave me the answer to how I should do things. And a lot of trial and error. A. lot. One of the challenges in the book I had to keep trying to figure out over the span of three days, each day trying different things for a couple of hours until I finally got it. That experience alone taught me very much.

Keep at it, trial and error, google, docs; those are things you learn from. This book is - as the authors say - not in-depth in any areas, so it is your responsibility to delve deeper. I’ve already expanded a few of the apps we develop over the course of the book, just because you learn a lot that way. Hang in there :slight_smile:[/quote]

Thanks for your input, its really help me. I am currently on my first step learning iOS programming (I start two months ago). however, I am on the right way, there is nothing wrong with me :smiley:


#6

I implemented my Bronze/Silver/Gold code pretty much the same as the original post with one addition. We want the “No More Items!” row to be at the bottom AND be present at all times. So, I added a method to prevent that row from being deleted. Here is my code:

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Prevent the "No More Items" row from being deleted
    if ([indexPath row] == [[[BNRItemStore sharedStore] allItems] count]) {
        return UITableViewCellEditingStyleNone;
    }
    else {
        return UITableViewCellEditingStyleDelete;
    }
}

#7

[quote=“dmddmd”]I had a very similar solution, with a minor difference to the resultant behavior to a user attempting to move a row below the “No more items!” row; rather than the moved row snapping back to it’s original location, it slides into the row just above the “No more items!” row.

- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath { if ([proposedDestinationIndexPath row] < [[[BNRItemStore sharedStore] allItems] count]) { return proposedDestinationIndexPath; } else { NSIndexPath *revisedIndexPath = [NSIndexPath indexPathForRow:([proposedDestinationIndexPath row] - 1) inSection:[proposedDestinationIndexPath section]]; return revisedIndexPath; } } [/quote]

This is beautiful. Many thanks.


#8

Another way to prevent user’s attempt to move an item to the end of the list is to put the item back to where it came from since the move is considered illegal.

- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
    return ([proposedDestinationIndexPath row]<[[[BNRItemStore sharedStore]allItems]count]) ? proposedDestinationIndexPath : sourceIndexPath;
}

EDIT: Oops just realize that the OP did the same thing!


#9

I love this book, the challenges and the forum. :smiley:

In my implementation, I implemented this delegate protocol method tableView:shouldIndentWhileEditingRowAtIndexPath:

- (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath { return YES; }
Otherwise, my last row does not indent with all the editable rows in editing mode.


#10

[quote=“sl0o0m”]
in all challenges I know what I should to do, but I couldn’t figure out How or what the method I should use and implement !! even if I search in API and documentation I couldn’t !! :frowning:[/quote]

Do not despair my friend. Cocoa usually makes our life beautiful. But if you could not find tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: by yourself, be assured that you were not alone. Any normal human-being cannot even guess if that method is for the data source protocol or delegate protocol.

My advice is to try the challenges hard, get familiar with the documentation, and practice UITableView with your own projects.

:arrow_right: “Repetition is the mother of skills”.


#11

Rindra, I loved this… since it was annoying me too about the last uneditable row not indenting with the others.

[quote=“rindra”]I love this book, the challenges and the forum. :smiley:

In my implementation, I implemented this delegate protocol method tableView:shouldIndentWhileEditingRowAtIndexPath:

- (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath { return YES; }
[/quote]
This makes it look much more like a “real” app. Thanks!

Edit Spoke too soon, this doesn’t work in my case, since I’ve already told it that I can’t edit that row. Maybe it works in the situation where people have the last row’s EditingStyle set to UITableViewCellEditingStyleNone but not for the other solution.


#12

It really looks great if you implement this:

- (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row != [[[BNRItemStore sharedStore]allItems]count])
    return NO;
}

#13

As I have said in another post, this only works for the very last item being a UITableViewCell that is a cell that cannot be moved. Load up the simulator and add some new Items, lets say 5 of them. Now you have 5 or 6 UITableViewCells that should always be on the bottom. What if you drag a cell in the middle of these? What you could have would be something like this:

Rusty Spork
No More items!
No More Items!
Rusty Bear
No more Items!
No More Items!

With ‘Rusty Bear’ being dragged into the middle. obviously the code to just check for the last one isn’t going to work :slight_smile:

I dont think the Gold challenge asked for this. perhaps this should have been a Gold+ challenge.


#14

Quick questions - does your Silver Challenge solution crashes if implemented like here AND when you try to either (a) DELETE the “No More Items” row or (b) move rows BELOW the “No More Items” row?

Just trying to see if other run into the same problem. Thanks, Jack


#15

That was a nice Saturday.
Here is what was happening in my brain from 10am to 2pm:

Bronze Challenge:
We go to the method that being called when we tap the “delete” button in “Editing” mode. This method is “tableView: commitEditingStyle: forRowAtIndexPath: ” in the xxxViewController.
Inside that method we pass a parameter of type “UITableViewCellEditingStyle” - UITableViewCellEditingStyleDelete.
Let’s delete a couple characters from the name of this argument and see if another parameter with value “Remove” will pop up. If not, we will go to the API reference.
No, besides the option “delete” there were options “insert” and “none”. No option “remove”. We go to the API reference and search for “UITableViewCellEditingStyle”.
API didn’t give us any info.
I click on the “Command” and on the “UITableViewCellEditingStyle” to reveal the UIKit Framework and the UITableViewCell.h declaration. I perform search by the document, searching for “delete”.

GENERAL NOTE: I have to read the frameworks files more often than the API files. There are lot of comments that our “Gods” from apple were willing to pass.

From the header file of UITableViewCell I found that UITableViewCell has a property “showingDeleteConfirmation”. It is a boolean property. There is a comment from Apple that says: These methods can be used by subclasses to animate additional changes to the cell when the cell is changing state. Note that when the cell is swiped, the cell will be transitioned into the “UITableViewCellStateShowingDeleteConfirmationMask” state,

but the “UITableViewCellStateShowingEditControlMask” will not be set. There are two methods after the note:

  • (void)willTransitionToState:(UITableViewCellStateMask)state;
  • (void)didTransitionToState:(UITableViewCellStateMask)state;

It says “These methods can be used to animate additional changes…” So we will use one of those methods. Probably “willTransitionToState…”
In this method we pass an argument of type “UITableViewCellStateMask” . From the same header file we know that there are three masks. They are declared in a structure. So one of those objects will be passed - in our case the “UITableViewCellStateShowingDeleteConfirmationMask” … I surrender and go the forum.

Ok, from the forum I learned that the method that I was looking for was located in the “UITableViewDelegate Protocol Reference”. As “rindra” said: “Any normal human-being cannot even guess if that method is for the data source protocol or delegate protocol.” . Therefore I can write the what steps I should take when tackling tasks from this book:
1. Decide on which class we need to perform action.
2. Go to API reference and read about this class. Study what properties and methods it has. Nothing? -> next step.
3. Command+Click on that class to reveal declaration in the framework. Nothing?->next step.
4. See if that class (or its parent) that we need to work with conforms to any protocols. Since protocols are a collections of methods, method that we are looking for might be in protocol. To find out which protocols our class conforms to - go to the header file and “Command+Click” on the parent class.

Can’t wait for my Sunday session!


#16

there is bag in this code.

do this. lunch the app, you have only "no more items"
add 2 or more rows.
edit rows, move some up and down.
click done button to finish editing.
click edit button again and the "no more item becomes editable.

I fount is strange, then i checked the allitems count to see whats going on.
and every time i moved any row, i got in [[[BNRItemStor sharedStore] allItems] count] increased with 1.

any idea why its happening?


#17

after 5 hours of try and error i came up with this lazy and simple solution.
i created a method which adds “No more item” string to all items;

//BNRItemStore.h
- (void)addNoMoreItem;

//BNRItemStore.m
- (void)addNoMoreItem
{
    [allItems insertObject:@"No more items!!!" atIndex:[allItems count]];
}

then in IteemsViewController.m im calling it in init

- (id)init 
{
    // Call the superclass's designated initializer
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {
        [[BNRItemStore defaultStore] addNoMoreItem];
    }
    return self;
}

now when we start we have no more item in table, but when we add raw, "no more item is not the last row any more. so I decided to change the added random item to be as follows

//BNRItemStore.m
- (BNRItem *)createItem
{
    BNRItem *p = [BNRItem randomItem];
    int i = [allItems count] - 1;
    [allItems insertObject:p atIndex:i];
   
    return p;
}

now every time we add new item it has one index less then “no more item”

then to disable the editing of the last row

//ItemsViewController.m
//add this method

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([indexPath row] == [[[BNRItemStore defaultStore] allItems] count]-1) {
        return NO;
    } else {
        return YES;
    }
}

to prevent moved rows under the last row i used this

//ItemsViewController.m
//add this method
-(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)from toProposedIndexPath:(NSIndexPath *)to
{
    if ([to row] == [[[BNRItemStore defaultStore] allItems] count]-1) {
        return from;
    } else {
        return to;
    }
    
}

hope this will help to someone :slight_smile: