Bronze Challenge


#1

This my solution to change the number of songs shown. A couple of issues I had were that my UIToolbar kept scrolling with my tableView and my last row in the table was obscured by the toolbar but I managed to sort these. Only issue is the position of the toolbar at the bottom. Works as expected on the ipad but is not at the very bottom on the iphone. I have written the code so that is should work out the bounds and then position it rather than hardcode in values. Am I missing something?

[code]#import “ListViewController.h”
#import “RSSChannel.h”
#import “RSSItem.h”
#import “WebViewController.h”
#import “ChannelViewController.h”
#import “BNRFeedStore.h”

@implementation ListViewController
@synthesize webViewController, songCount;

-(id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];

if (self) {

    UIBarButtonItem *bbi =
    [[UIBarButtonItem alloc] initWithTitle:@"Info"
                                     style:UIBarButtonItemStyleBordered
                                    target:self
                                    action:@selector(showInfo:)];
    
    [[self navigationItem] setRightBarButtonItem:bbi];
    
    UISegmentedControl *rssTypeControl = [[UISegmentedControl alloc]initWithItems:
                                          [NSArray arrayWithObjects:@"BNR", @"Apple", nil]];
    
    [rssTypeControl setSelectedSegmentIndex:0];
    [rssTypeControl setSegmentedControlStyle:UISegmentedControlStyleBar];
    [rssTypeControl addTarget:self action:@selector(changeType:) forControlEvents:UIControlEventValueChanged];
    [[self navigationItem]setTitleView:rssTypeControl];
    
    songCount = 10;
    
    [self fetchEntries];
}
return self;

}

-(void)viewDidLoad
{
//create space at the bottom of the tableview to put a toolbar. This prevents the last table row from becoming obscured.
footer = [[UIView alloc] initWithFrame:CGRectMake(0, [[self view]bounds].size.height - 50, [[self view]bounds].size.width, 50)];
footer.backgroundColor = [UIColor clearColor];
self.tableView.tableFooterView = footer;

//create the toolbar
toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, [[self view]bounds].size.height -50, [[self view]bounds].size.width, 50)];

//create the label for the toolbar
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(10, 0, 200, 50)];
[label setText:[NSString stringWithFormat:@"Number Of Songs:"]];
[label setBackgroundColor:[UIColor clearColor]];

//the label and buttons have to be added as uibarbuttonitems in order to add to the array of toolbar.
subtractSong = [[UIBarButtonItem alloc]initWithTitle:@"-" style:UIBarButtonItemStyleBordered target:self action:@selector(songNumber:)];
addSong = [[UIBarButtonItem alloc]initWithTitle:@"+" style:UIBarButtonItemStyleBordered target:self action:@selector(songNumber:)];
UIBarButtonItem *text = [[UIBarButtonItem alloc]initWithCustomView:label];

//add the 3 barbuttonitems to the toolbar
[toolbar setItems:[NSArray arrayWithObjects:text, subtractSong, addSong, nil]];

[[self navigationController].view addSubview:toolbar];

footer.hidden = YES;
toolbar.hidden = YES;

}

  • (void)showInfo:(id)sender
    {
    // Create the channel view controller
    ChannelViewController *channelViewController = [[ChannelViewController alloc]
    initWithStyle:UITableViewStyleGrouped];

    if ([self splitViewController]) {

      UINavigationController *nvc = [[UINavigationController alloc]
                                     initWithRootViewController:channelViewController];
      
      // Create an array with our nav controller and this new VC's nav controller
      NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],
                      nvc,
                      nil];
      
      // Grab a pointer to the split view controller
      // and reset its view controllers array.
      [[self splitViewController] setViewControllers:vcs];
      
      // Make detail view controller the delegate of the split view controller - ignore this warning
      [[self splitViewController] setDelegate:channelViewController];
      
      // If a row has been selected, deselect it so that a row
      // is not selected when viewing the info
      NSIndexPath *selectedRow = [[self tableView] indexPathForSelectedRow];
      if (selectedRow)
          [[self tableView] deselectRowAtIndexPath:selectedRow animated:YES];
    

    } else {
    [[self navigationController] pushViewController:channelViewController
    animated:YES];
    }

    // Give the VC the channel object through the protocol message
    [channelViewController listViewController:self handleObject:channel];
    }

  • (void)changeType:(id)sender
    {
    rssType = [sender selectedSegmentIndex];

    if (rssType == ListViewControllerRSSTypeBNR) {
    footer.hidden = YES;
    toolbar.hidden = YES;

    } else if (rssType == ListViewControllerRSSTypeApple){
    footer.hidden = NO;
    toolbar.hidden = NO;
    }
    [self fetchEntries];
    }

  • (void)tableView:(UITableView *)tableView
    didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    //if not in a split view controller (when using iPhone) then push the webviewcontroller onto navigation controller stack. If in splitviewcontroler (using iPad) then just let the uiSplitViewController to place the webviewcontroller on screen
    if (![self splitViewController]) {
    // Push the web view controller onto the navigation stack - this implicitly
    // creates the web view controller’s view the first time through
    [[self navigationController] pushViewController:webViewController animated:YES];
    }
    else {
    // We have to create a new navigation controller, as the old one
    // was only retained by the split view controller and is now gone
    UINavigationController *nav =
    [[UINavigationController alloc] initWithRootViewController:webViewController];

      NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],
                      nav,
                      nil];
      
      [[self splitViewController] setViewControllers:vcs];
      
      // Make the detail view controller the delegate of the split view controller
      [[self splitViewController] setDelegate:webViewController];
    

    }

    // Grab the selected item
    RSSItem *entry = [[channel items] objectAtIndex:[indexPath row]];

    [webViewController listViewController:self handleObject:entry];
    }

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

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

return cell;

}

  • (void)fetchEntries
    {
    //Get hold of the segmented control that is currently in the title view
    UIView *currentTitleView = [[self navigationItem]titleView];

    //create an activity indicator and start it spinning in the nav bar
    UIActivityIndicatorView *aiView = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
    [[self navigationItem] setTitleView:aiView];
    [aiView startAnimating];

    void(^completionBlock)(RSSChannel *obj, NSError *err) = ^(RSSChannel *obj, NSError *err) {

      //when the request completes - success or failure - replce the activity indicator with the segmented control
      [[self navigationItem] setTitleView:currentTitleView];
      
      if (!err) {
          //if everything went ok, grab the channel object, and relod the table.
          channel = obj;
          
          [[self tableView]reloadData];
      } else {
          //if things went bad, show an alert view
          NSString *errorString = [NSString stringWithFormat:@"Fetch failed: %@", [err localizedDescription]];
          
          UIAlertView *av  =[[UIAlertView alloc]initWithTitle:@"Error" message:errorString delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
          [av show];
      }
    

    };

    //initiate the request
    if (rssType == ListViewControllerRSSTypeBNR) {
    [[BNRFeedStore sharedStore]fetchRSSFeedWithCompletion:completionBlock];

    } else if (rssType == ListViewControllerRSSTypeApple){
    [[BNRFeedStore sharedStore]fetchTopSongs:songCount withCompletion:completionBlock];
    }
    }

//songNumber is called when the + or - is pressed. It takes the new value and assigns that to songCount and then calls the fetchEntries method again to display the new data

  • (void)songNumber: (UIBarButtonItem *)bbi
    {
    if (bbi == subtractSong) {
    songCount–;
    } else if (bbi == addSong) {
    songCount++;
    }
    [self fetchEntries];
    }

  • (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io
    {
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
    return YES;
    return io == UIInterfaceOrientationPortrait;
    }

@end
[/code]

NOTE I HAVE EDITED THIS ON 14/01/14 DUE TO A FEW ISSUES I FOUND


#2

Just thought I’d mention about reading the json data. I used an online reader jsonviewer.stack.hu/ which made it a lot easier to see what was going on.