Bronze Challenge Solution


#1

Basically, just followed the subclassing procedure we did in Chapter 15 by adding a DetailTableCell h & m and then creating an empty XIB file for DetailTableCell.xib. Then just added the necessary code to RSSItem.m to load the necessary data. Finally, edited the code in ListViewController to pull in the xib. Here’s the code.

DetailTableCell.h (No implementation need for DetailTableCell)

@interface DetailTableCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *authorLabel;
@property (weak, nonatomic) IBOutlet UILabel *categoryLabel;

@end

RSSItem.h

@interface RSSItem : NSObject <NSXMLParserDelegate>
{
    NSMutableString *currentString;
}

@property (nonatomic, weak)id parentParserDelegate;

@property (nonatomic, strong)NSString *title;
@property (nonatomic, strong)NSString *link;
@property (nonatomic, strong)NSString *author;
@property (nonatomic, strong)NSString *category;

@end

RSSItem.m

#import "RSSItem.h"

@implementation RSSItem

@synthesize parentParserDelegate, title, link, category, author;

-(void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
 namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
   attributes:(NSDictionary *)attributeDict
{
    NSLog(@"t\t\%@ found a %@ element", self, elementName);
    
    if ([elementName isEqualToString:@"title"])
    {
        currentString = [[NSMutableString alloc] init];
        [self setTitle:currentString];
    }
    else if ([elementName isEqualToString:@"link"])
    {
        currentString = [[NSMutableString alloc] init];
        [self setLink:currentString];
    }
    else if ([elementName isEqualToString:@"author"])
    {
        currentString = [[NSMutableString alloc] init];
        [self setAuthor:currentString];
    }
    else if ([elementName isEqualToString:@"category"])
    {
        currentString = [[NSMutableString alloc] init];
        [self setCategory:currentString];
    }
}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    [currentString appendString:string];
}

-(void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
 namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
    currentString = nil;
    
    if ([elementName isEqualToString:@"item"])
    {
        [parser setDelegate:parentParserDelegate];
    }
}

@end

ListViewController.m

#import "ListViewController.h"
#import "RSSChannel.h"
#import "RSSItem.h"
#import "WebViewController.h"
#import "DetailTableCell.h"

@implementation ListViewController
@synthesize webViewController;

-(id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    
    if (self)
    {
        [self fetchEntries];
    }
    
    return self;
}

-(void)viewDidLoad
{
    [super viewDidLoad];
    
    UINib *nib = [UINib nibWithNibName:@"DetailTableCell" bundle:nil];
    
    [[self tableView] registerNib:nib forCellReuseIdentifier:@"DetailTableCell"];
}

-(void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
 namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
   attributes:(NSDictionary *)attributeDict
{
    NSLog(@"%@ found a %@ element", self, elementName);
    
    if ([elementName isEqualToString:@"channel"])
    {
        channel = [[RSSChannel alloc] init];
        [channel setParentParserDelegate:self];
        
        [parser setDelegate:channel];
    }
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [[channel items] count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    
//    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    DetailTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"DetailTableCell"];
    if (cell == nil)
    {
        cell = [[DetailTableCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:@"DetailTableCell"];
    }
    
    RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];
    [[cell titleLabel] setText:[item title]];
    [[cell authorLabel] setText:[item author]];
    [[cell categoryLabel] setText:[item category]];
    
    return cell;
}

//Rest of code the same

#2

Here’s without using an XIB:

RSSItem.h:

[code]#import <Foundation/Foundation.h>

@interface RSSItem : NSObject
{
NSMutableString *currentString;
}

@property (nonatomic, weak) id parentParserDelegate;

@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *link;
@property (nonatomic, strong) NSString *author;
@property (nonatomic, strong) NSString *category;

@end
[/code]

RSSItem.m:

[code]#import “RSSItem.h”

@implementation RSSItem

@synthesize title, link, author, category, parentParserDelegate;

  • (void)parser:(NSXMLParser *)parser
    didStartElement:(NSString *)elementName
    namespaceURI:(NSString *)namespaceURI
    qualifiedName:(NSString *)qName
    attributes:(NSDictionary *)attributeDict
    {
    WSLog(@"\t\t%@ found a %@ element", self, elementName);
    if ([elementName isEqual:@“title”]) {
    currentString = [[NSMutableString alloc] init];
    [self setTitle:currentString];
    }
    else if ([elementName isEqual:@“link”]) {
    currentString =[[NSMutableString alloc] init];
    [self setLink:currentString];
    }
    else if ([elementName isEqual:@“author”]) {
    currentString =[[NSMutableString alloc] init];
    [self setAuthor:currentString];
    }
    else if ([elementName isEqual:@“category”]) {
    currentString =[[NSMutableString alloc] init];
    [self setCategory:currentString];
    }
    }

  • (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)str
    {
    [currentString appendString:str];
    }

  • (void)parser:(NSXMLParser *)parser
    didEndElement:(NSString *)elementName
    namespaceURI:(NSString *)namespaceURI
    qualifiedName:(NSString *)qName
    {
    currentString = nil;

    if ([elementName isEqual:@“item”]) {
    [parser setDelegate:parentParserDelegate];
    }
    }

@end
[/code]

UITableViewCell subclass, RSSItemCell.h:

#import <UIKit/UIKit.h>

@interface RSSItemCell : UITableViewCell

@property (strong, nonatomic) UILabel *titleLabel;
@property (strong, nonatomic) UILabel *authorLabel;
@property (strong, nonatomic) UILabel *categoryLabel;

@end

RSSItemCell.m:

#import "RSSItemCell.h"

@implementation RSSItemCell

@synthesize titleLabel, authorLabel, categoryLabel;

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
        //[[self textLabel] removeFromSuperview];
        CGRect msBounds = [[UIScreen mainScreen] bounds];
        titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, msBounds.size.width/3-5, 50)];
        authorLabel = [[UILabel alloc] initWithFrame:CGRectMake(msBounds.size.width/3+5, 0, msBounds.size.width/3-5, 50)];
        categoryLabel = [[UILabel alloc] initWithFrame:CGRectMake(2*msBounds.size.width/3+10, 0, msBounds.size.width/3-5, 50)];
        [titleLabel setBackgroundColor:[UIColor redColor]]; // To make it clear that I did get three labels. 
        [authorLabel setBackgroundColor:[UIColor grayColor]];
        [categoryLabel setBackgroundColor:[UIColor blueColor]];
        [self addSubview:titleLabel];
        [self addSubview:authorLabel];
        [self addSubview:categoryLabel];
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated];

    // Configure the view for the selected state
}

@end

In ListViewController.m, the tableView:cellForRowAtIndexPath: method:

[code]- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RSSItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@“RSSItemCell”];
if (cell == nil) {
cell = [[RSSItemCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@“RSSItemCell”];
}
RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];

[[cell titleLabel] setText:[item title]];
[[cell authorLabel] setText:[item author]];
[[cell categoryLabel] setText:[item category]];

return cell;

}
[/code]


#3

I solved the challenge using the same approach as yours . Now i wanted to add another functionality. I don’t want the author and category labels in the custom cells to be actionable i.e whenever a user will tap on either of these labels, the webviewcontroller should not load its view. In order to achieve this, i added 2 buttons, one on top of the “author” label and the other one on “category” label. The size of both the buttons matches that of the labels that they overlay. I have set their type as custom so that they do not obscure the labels underneath them. I created two IBAction methods, one for the button on top of “author” label and other for the button on top of “category” label.

Here is the code
Cell.h

[code]#import <UIKit/UIKit.h>

@interface Cell : UITableViewCell
@property (weak, nonatomic) IBOutlet UILabel *authorLabel;

@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *categoryLabel;

  • (IBAction)authorTapped:(id)sender; //not actionable

  • (IBAction)categoryTapped:(id)sender;//not actionable

@end[/code]

Cell.m

[code]#import “Cell.h”

@implementation Cell

  • (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
    {
    NSLog(@“Cell init”);
    self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
    if (self) {
    // Initialization code
    }
    return self;
    }

  • (IBAction)authorTapped:(id)sender {
    NSLog(@“Author tapped”);
    return;
    }

  • (IBAction)categoryTapped:(id)sender {
    NSLog(@“Category tapped”);
    return;
    }
    @end[/code]

Both the action methods are supposed to do nothing when the button that they are set up for, are tapped.
However, during runtime, when i tap on either of the labels (i.e author, category) the entire row does get selected and the webviewcontroller gets pushed on top of the “ListViewController”. Thats coz these two action methods are not being even called when i tap the invisible buttons. Neither of the two NSLog messages in the action methods are showing up in the console. WebVIew should only load when the “Title label” that is in the middle of the cell, is tapped onto and not the two corner labels ie.“author” and “category”. What is wrong with my implementation. Pls assist.