Reloading a table view after NSURLConnection succeeds - objective-c

I'm new to Xcode, so bear with me:
I have a table view that I'm trying to reload once the NSURLConnection succeeds. I have a number of messages that help me guide me along the way... but when I call the reload upon the table view, the table doesn't repopulate.
JsonViewController.h:
#import <UIKit/UIKit.h>
#interface JsonViewController : UITableViewController {
NSMutableArray *theTweets;
IBOutlet UITableView *tview;
NSMutableData *responseData;
}
#property (nonatomic, retain) NSMutableArray *theTweets;
#property (nonatomic, retain) UITableView *tview;
#end
JsonViewController.m:
#import "JsonViewController.h"
#import "SBJson.h"
#implementation JsonViewController
#synthesize theTweets;
#synthesize tview;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void) dealloc {
[theTweets release];
[super dealloc];
}
- (NSMutableArray*)theTweets {
return [[theTweets retain] autorelease];
}
- (void) setTheTweets:(NSMutableArray *)newTweets {
if (newTweets != theTweets) {
[newTweets retain];
[theTweets release];
theTweets = newTweets;
NSLog(#"Setting new tweets...");
[tview reloadData];
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
tview.delegate = self;
responseData = [[NSMutableData data] retain];
theTweets = [NSMutableArray array];
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://search.twitter.com/search.json?q=AriaPoker&result_type=recent"]];
[[NSURLConnection alloc] initWithRequest: request delegate:self];
NSLog(#"Trying to get feed upon initialization");
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
// methods that are not important
#pragma mark - Table view data source
- (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.
NSLog(#"Number of the tweets count at this point: %d", [theTweets count]);
return [theTweets count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"Number of the tweets count at this point: %d", [theTweets count]);
// Configure the cell...
NSDictionary *aTweet = [theTweets objectAtIndex:[indexPath row]];
//cell.textLabel.text = [aTweet objectForKey:#"text"];
cell.textLabel.adjustsFontSizeToFitWidth = YES;
cell.textLabel.font = [UIFont systemFontOfSize:12];
cell.textLabel.numberOfLines = 4;
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.text = #"Test";
cell.detailTextLabel.text = #"haha";
//NSURL *url = [NSURL URLWithString:[aTweet objectForKey:#"profile_image_url"]];
//NSData *data = [NSData dataWithContentsOfURL:url];
//cell.imageView.image = [UIImage imageWithData:data];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
NSLog(#"Loading cells in table");
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
}
#pragma mark NSURLConnection Delegate Methods
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
//do nothing
NSLog(#"A connection error has occurred!");
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSDictionary *results = [[responseString JSONValue] retain];
NSLog(#"Number of Rows: %d", [results count]);
NSMutableArray *allTweets = [results objectForKey:#"results"];
//[viewController setTweets:allTweets];
theTweets = allTweets;
NSLog(#"Number of misc2: %d", [theTweets count]);
[results release];
[tview reloadData];
}
#end
I'm wondering what I'm doing wrong here.

In connectionDidFinishLoading change from this:
theTweets = allTweets;
to this:
self.theTweets = allTweets;
or this way if you prefer:
[self setTheTweets:allTweets];
You weren't invoking the setter method, so it wasn't getting retained.

As suggested by progrmr try to call the setter method, or simply change definition of theTweets property to #dynamic theTweets in this case when you try to set property, the custom setter method will be called.

Related

UITableViewController with UISearchDisplayController not reloading data

I have a ViewController class with an appropriate .XIB file. Here is the ViewController code:
ViewController.h:
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface ViewController : UIViewController
{
NSArray *news;
NSMutableData *data;
}
#property (strong, nonatomic) IBOutlet UITableView *mainTableView;
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (strong, nonatomic) Results *result;
#end
ViewController.m:
#import "ViewController.h"
#import "Results.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize result, mainTableView, searchBar;
-(id) init
{
self = [super initWithNibName:#"ViewController_iPhone" bundle:nil];
if (self)
{
result = [[Results alloc] init];
mainTableView = [[UITableView alloc] init];
[self.mainTableView setDelegate:self];
}
return self;
}
- (void)viewDidLoad
{
self.title = #"Search";
[[UINavigationBar appearance] setTintColor:[UIColor blackColor]];
[super viewDidLoad];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSString *query = searchBar.text;
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://samplesite.com/external/metasearchjson.php?query=%#", query]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
data = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
[data appendData:theData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *responseDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:NULL];
if ([responseDict isKindOfClass:[NSArray class]]) {
news = responseDict;
//[mainTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
[[self mainTableView] reloadData];
NSLog(#"%#", mainTableView);
} else {
NSLog(#"JSON Error.");
}
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:[indexPath row] inSection:0]];
NSString *url = _getString([[news objectAtIndex:[indexPath row]] objectForKey:#"link"]);
[result getURL:url];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self.navigationController pushViewController:result animated:YES];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
UIAlertView *errorView = [[UIAlertView alloc] initWithTitle:#"Error" message:#"The download could not complete - please make sure you're connected to either 3G or Wi-Fi." delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[errorView show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [news count];
}
NSString *_getString(id obj)
{
return [obj isKindOfClass:[NSString class]] ? obj : nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainCell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MainCell"];
}
cell.detailTextLabel.text = _getString([[news objectAtIndex:indexPath.row] objectForKey:#"metaScore"]);
cell.textLabel.text = _getString([[news objectAtIndex:indexPath.row] objectForKey:#"title"]);
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
My connections are all working, and the code successfully puts the JSON data into the UITableView.
My problem is, the table view isn't reloading!
I have tried to just load it without a UISearchDisplayController, and it works fine. I'm thinking it's some sort of override. Where my TableView reloads data, that just doesn't work. What's also weird is that if you type in something to the search display, the table view is displayed. What am I doing wrong that the Search Bar doesn't reload the data?
In your search display delegate, you need to implement
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
and return YES.
Try make next steps:
In ViewController.h:
add protocols:
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
add property:
#property (strong, nonatomic) NSArray* news;
then replace in connectionDidFinishLoading: method news = responseDict; as
self.news = responseDict;
then perform reloadData in main thread:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
...
dispatch_async(dispatch_get_main_queue(), ^{
[[self mainTableView] reloadData];
});
in ViewController.m insert line [self.mainTableView setDataSource: self]; :
-(id) init
{
self = [super initWithNibName:#"ViewController_iPhone" bundle:nil];
if (self)
{
...
[self.mainTableView setDelegate:self];
[self.mainTableView setDataSource: self];
}
return self;
}
hope it helps you.

ViewController load methods called when view disappears?

I've got the following code in a viewController. I've got a NavigationController on the view (which is the child view - the code for the parent is working fine)
What happens is when I select an option on the parent, this viewController loads. The user can select an option from the child viewController to open a PDF file with a DocumentInteractionController (which works fine).
The problem is when I try going back to the parent viewController, messages are being sent to the child viewController as if it's still allocated. I saw something similar when I set it up since there were multiple calls to the methods in the child viewController.
Any thoughts on what I'm doing wrong?
#import "DetailViewController.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
#synthesize node;
#synthesize replies;
#synthesize docController;
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
[self.tableView setContentOffset:CGPointZero animated:NO];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.docController init];
// Do any additional setup after loading the view from its nib.
}
- (void) dealloc
{
[self.docController release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.replies == nil)
{
self.replies = [[NSArray alloc] init];
self.actions = [[NSArray alloc] init];
}
if(self.replies.count == 0)
{
self.replies = [self.node nodesForXPath:#"./question/reply/text" error:nil];
self.actions = [self.node nodesForXPath:#"./question/reply/response/action" error:nil];
}
return self.replies.count;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"QuestionCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Get the object to display and set the value in the cell
NSString *cellText = [[replies objectAtIndex:indexPath.row] stringValue];
cell.textLabel.text = cellText;
return cell;
}
- (void) showOptionsMenu:(NSString *) fileName
{
NSString *fileToOpen = [[NSBundle mainBundle] pathForResource:fileName ofType:#"pdf"];
NSURL *fileURL = [NSURL fileURLWithPath:fileToOpen];
self.docController = [self setupControllerWithURL:fileURL usingDelegate:self];
bool didShow = [self.docController presentOptionsMenuFromRect:CGRectMake(0, 0, 150, 150) inView: self.view animated:YES];
if(!didShow)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Sorry, app not found" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *action = [[self.actions objectAtIndex:indexPath.row] stringValue];
[self showOptionsMenu:action];
}
- (UIDocumentInteractionController *) setupControllerWithURL: (NSURL *) fileURL usingDelegate:(id <UIDocumentInteractionControllerDelegate>) interactionDelegate
{
UIDocumentInteractionController *interactionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
interactionController.delegate = interactionDelegate;
return interactionController;
}
#end
EDIT
Adding the code for the parent view controller...maybe there's something I'm doing wrong in there? I'm using GDataXML to load a Q&A app based on the contents of an XML file...
#implementation ViewController
#synthesize currentReply;
#synthesize questions;
- (void)viewDidLoad
{
[super viewDidLoad];
[self setUpQuestions];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc
{
[super dealloc];
}
- (void) setUpQuestions
{
// create and init NSXMLParser object
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"query" ofType:#"xml"];
NSData *xml_data = [[NSData alloc] initWithContentsOfFile:filePath];
NSError *error;
GDataXMLDocument *xmlDoc = [[GDataXMLDocument alloc] initWithData:xml_data options:0 error:&error];
NSArray *rootDataArray = [xmlDoc.rootElement nodesForXPath:#"//query" error:nil];
for (GDataXMLElement *rootDataElement in rootDataArray)
{
// Allocate the query object
self->query = [[[Query alloc] init] autorelease];
// Name
NSArray *query_title = [rootDataElement elementsForName:#"text"];
if (query_title.count > 0)
{
GDataXMLElement *queryTitle = (GDataXMLElement *) [query_title objectAtIndex:0];
self->query.queryTitle = [[[NSString alloc] initWithString:queryTitle.stringValue] autorelease];
}
NSArray *query_first_question = [rootDataElement elementsForName:#"question"];
NSArray *replies = [NSArray alloc];
questions = [[NSMutableArray alloc] init];
if(query_first_question.count == 1)
{
GDataXMLElement *fq = (GDataXMLElement *) [query_first_question objectAtIndex:0];
replies = [fq elementsForName:#"reply"];
for (GDataXMLElement *replyElement in replies)
{
[questions addObject:replyElement];
}
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Only one section.
return 1;
}
- (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection:(NSInteger)section
{
switch(section)
{
case 0:
return questions.count;
break;
case 1:
return 1;
break;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"QuestionCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
// Get the object to display and set the value in the cell.
GDataXMLElement *questionAtIndex = questions[indexPath.row];
NSString *cellText = [[[questionAtIndex elementsForName:#"text"] objectAtIndex:0] stringValue];
cell.textLabel.text = cellText;
//cell.textLabel.text = [[questionAtIndex elementsForName:#"text"] objectAtIndex:0];
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSMutableString *msg = [NSMutableString new];
//[msg appendString:#"You selected row: "];
//[msg appendString:[NSString stringWithFormat:#"%i",indexPath.row]];
//UIAlertView *alertMsg = [[UIAlertView alloc] initWithTitle:#"Row Selected" message:msg delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
//[alertMsg show];
if (questions != nil)
{
GDataXMLElement *selectedReply = (GDataXMLElement *) [questions objectAtIndex:indexPath.row];
DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
dvc.node = selectedReply;
[self.navigationController pushViewController:dvc animated:YES];
[dvc release];
}
}
EDIT
I've tried profiling and looking for zombies, but when the crash occurs there are no zombie objects flagged. It throws the following error in the console:
[UIView _forgetDependentConstraint:]: message sent to deallocated instance 0x1e8ab810
I have seen this Issue before also !!!
Answer : Turn Off "AutoLayout".
I am guessing the error occurred due to new feature in ios called AutoLayout. It looks like Compiler has created some NSLayoutConstraint objects and due to some reason the objects were released more than they should. Deletion and Re-Creation, forces Xcode to Re-Build the Constraints. But,I am not 100% sure.
Try to Un-Check "AutoLayout", if it can solve your Problem.
Your DetailViewController code is fine - not actually fine, as you're leaking self.replies and self.actions, and the [self.docController init] is very odd and probably wrong (always alloc and init together) - but the lifecycle code on this end looks fine. The problem is almost certainly in the parent view controller (or possibly the document controller if you're creating a retain cycle there). If the parent view controller is holding onto a pointer to the detail view controller, it won't actually be deallocated and accessing the view or any property thereof will cause -viewDidLoad to be called again.
From what I understood, your parent view controller is setting the node here:
dvc.node = selectedReply;
and it's never being released from your DetailViewController.
I'm assuming that your GDataXMLElement in the DetailViewController header is set as "retain".
And there's some leaking problems as icodestuff pointed out.

Problems Populating TableView with NSMutableArray

I'm using SudzC for an iPhone app. I'm succesfully calling my ASP.Net webservice and pulling the needed data into an NSMutable array called tableData. I have a tableView that should display the contents of tableData; however this is not working. I've looked for hours at this problem. I'm very new to the Objective C world so I'm thinking it's a small oversight on my part. I've linked my TableView to the ViewController (delegate/datasource) as well.
Here's is the code I have:
#import "ViewController.h"
#import "DocumentServiceJSONService.h"
#interface ViewController ()
#end
#implementation ViewController
NSMutableArray *tableData;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)viewWillAppear:(BOOL) animated {
tableData = [[NSMutableArray alloc] init];
DocumentServiceJSONService* service = [[DocumentServiceJSONService alloc] init];
[service GetAllEMSDocumentsXMLAsString: self action:#selector(handleGetAllEMSDocuments:)];
[super viewWillAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) handleGetAllEMSDocuments:(id) result {
if ([result isKindOfClass:[NSError class]]) {
//if an error has occurred, handle it
return;
}
if([result isKindOfClass: [SoapFault class]]) {
return;
}
NSString* xmlString = result;
CXMLDocument *xmlDoc = [[CXMLDocument alloc] initWithXMLString:xmlString options:0 error:nil];
NSArray *nodes = NULL;
nodes = [xmlDoc nodesForXPath:#"//EMSDocuments" error:nil];
for (CXMLElement *node in nodes) {
NSMutableDictionary *item = [[NSMutableDictionary alloc] init];
int counter;
for (counter = 0; counter < [node childCount]; counter++) {
[item setObject:[[node childAtIndex:counter] stringValue] forKey:[[node childAtIndex:counter] name]];
}
//[item setObject:[[node attributeForName:#"DisplayName"] stringValue] forKey:#"DisplayName"];
NSString *displayName = [item objectForKey:#"DisplayName"];
[tableData addObject:displayName];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
#end
Any advice is appreciated.
Make sure you have in your .h the delegate and datasource correctly set:
#interface YourViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
And if you are using XIBs for the user interface, that the tableview is connected to the File's Owner as datasource and delegate.
--
EDIT:
Creating a Outlet for you tableview:

How to make my UITableViewCell not just have a title and a subtitle, but a title, subtitle (date), and another label (summary)?

Trying to make an my RSS Feed app have the summary inside the label, I have parsed the description (summary), but I don't understand how to add it in the UITableViewCell. I am currently using UITableViewCellStyleSubtitle, so I can show the title of each post and the date below it. How do I add one more below it to show summary of each post?
MasterViewController.h (TableView Header)
#import <UIKit/UIKit.h>
#class RSSChannel;
#class WebViewController;
#interface MasterViewController : UITableViewController
<NSXMLParserDelegate>
{
NSURLConnection *connection;
NSMutableData *xmlData;
RSSChannel *channel;
}
- (void)fetchEntries;
#property (nonatomic, strong) WebViewController *webViewController;
#end
MasterViewController.m (TableView Implementation)
#import "MasterViewController.h"
#import "RSSChannel.h"
#import "RSSItem.h"
#import "WebViewController.h"
#interface MasterViewController () {
NSMutableArray *_objects;
}
#end
#implementation MasterViewController
#synthesize webViewController;
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self fetchEntries];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)insertNewObject:(id)sender
{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#pragma mark - Table View
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"The amount of items in the table: %u", [[channel items] count]);
return [[channel items] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:#"UITableViewCell"];
}
RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];
[[cell textLabel] setText:[item title]];
[[cell detailTextLabel] setText:[item date]];
return cell;
}
- (void)fetchEntries
{
// Create a new data container for the stuff that comes back from the service
xmlData = [[NSMutableData alloc] init];
// Construct a URL that will ask the service for what you want -
// Note we can concatenate literal strings together on multiple lines in this way it
// results in a single NSString instance
NSURL *url = [NSURL URLWithString:
#"http://sephardijews.com/feed/"];
// Putting the URL we made into an NSURLRequest, so we can connect to the url data that we specifed
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Creating a connecting that will exchange this request for the data from the URL we specifed
connection = [[NSURLConnection alloc] initWithRequest:req
delegate:self
startImmediately:YES];
}
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifedName
attributes:(NSDictionary *)attributeDict
{
NSLog(#"%# found a %# element", self, elementName);
if ([elementName isEqual:#"channel"]) {
// If the parser saw a channel, create new instance, store in our ivar
channel = [[RSSChannel alloc] init];
// Give the channel object a pointer back to ourselves for later
[channel setParentParserDelegate:self];
// Set the parser's delegate to the channel object
[parser setDelegate:channel];
}
}
// Method used to start at the beginning of the instance of the tableview, it will intialize the connection method to retrieve the posts from the URL
/* - (id) initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
[self fetchEntries];
}
return self;
}
*/
// This method will be called several times as the data arrives
- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
// Add the incoming chunk of data to the container we are keeping
// The data always comes in the correct order
[xmlData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn
{
// Create the parser object with the data received from the web service
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
// Give it a delegate - don't worry about the warning
[parser setDelegate:self];
// Tell it to start parsing - the documet will be parsed and the delegate of NSXMLParser will get all of its delegate messages sent to it before this line finishes execution - it is blocking
[parser parse];
// Get rid of the XML data as we no longer need it
xmlData = nil;
// Get rid of the connection, no longer need it
connection = nil;
// Reload the table
[[self tableView] reloadData];
NSLog(#"%#\n %#\n %#\n", channel, [channel title], [channel infoString]);
}
- (void)connection:(NSURLConnection *)conn didFailWithError:(NSError *)error
{
// Release the connection object, we are done with it cause' there is no connection
// Setting the connection to nil will stop the connection because it is nothing/0
connection = nil;
// Release the xmlData object. We stopped to connection to put the data in the xmlData object, so we set it to nil also
xmlData = nil;
// Grab the description of the error object passed to us, so we can tell the user
NSString *errorString = [NSString stringWithFormat:#"Fetch failed: %#", [error localizedDescription]];
// Create and show an alert view to the user with the error string to tell them the error in the process of the connection
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error"
message:errorString
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 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];
//Grab the selected item
RSSItem *entry = [[channel items] objectAtIndex:[indexPath row]];
//Constructs a URL with the link string of the item
NSURL *url = [NSURL URLWithString:[entry link]];
// Construct a request object with that URL
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Load the request into the web view
[[webViewController webView] loadRequest:req];
// Set the title of the web view controller's navifation item
[[webViewController navigationItem] setTitle:[entry title]];
}
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#end
Try changing the UITableViewCellStyle to custom and dragging in your own UITextLabel objects to the prototype cell. Make #propertys for each of your data items in your TableViewController...
#interface MasterViewController ()
NSMutableArray *_objects;
#property (nonatomic, strong) IBOutlet UILabel *title;
#property (nonatomic, strong) IBOutlet UILabel *date;
#property (nonatomic, strong) IBOutlet UILabel *summary;
#end
Then connect the UITextLabels to their respective data.

Invalid argument type to unary expression

I have this program with a tableView as my first view. I have also implemented (or at least tried to) a search bar on top of the view. Have used several hours to search for a solution, but without positive results.
#import "FirstViewController.h"
#import "NSDictionary-MutableDeepCopy.h"
#implementation FirstViewController
#synthesize listData, table, search, allNames, names, keys;
#pragma mark -
#pragma mark Custom Methods
- (void)resetSearch {
NSMutableDictionary *allNamesCopy = [self.allNames mutableDeepCopy];
self.names = allNamesCopy;
[allNamesCopy release];
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObjectsFromArray:[[self.allNames allKeys]
sortedArrayUsingSelector:#selector(compare:)]];
self.keys = keyArray;
[keyArray release];
}
-(void)handleSearchForTerm:(NSString *)searchTerm {
NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init];
[self resetSearch];
for (NSString *key in self.keys) {
NSMutableArray *array = [names valueForKey:key];
NSMutableArray *toRemove = [[NSMutableArray alloc] init];
for (NSString *name in listData) {
if ([name rangeOfString:searchTerm
options:NSCaseInsensitiveSearch].location == NSNotFound)
[toRemove addObject:name];
}
if ([array count] == [toRemove count])
[sectionsToRemove addObject:key];
[array removeObjectsInArray:toRemove];
[toRemove release];
}
[self.keys removeObjectsInArray:sectionsToRemove];
[sectionsToRemove release];
[table reloadData];
}
- (void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"sortednames" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.names = dict;
self.allNames = dict;
[dict release];
[self resetSearch];
[table reloadData];
[table setContentOffset:CGPointMake(0.0, 44.0)animated:NO];
self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
NSArray *array = [[NSArray alloc] initWithObjects:
// A larger amount of objects here.
self.listData = array;
[array release];
[super viewDidLoad];
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.listData = nil;
self.table = nil;
self.search = nil;
self.allNames = nil;
self.names = nil;
self.keys = nil;
}
- (void)dealloc {
[listData release];
[search release];
[table release];
[allNames release];
[keys release];
[names release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return ([keys count] > 0) ? [keys count] : 1;
}
- (NSInteger)tableView:(UITableView *)aTableView
numberOfRowsInSection: (NSInteger)section {
return [self.listData count];
if ([keys count] == 0)
return 0;
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
return [nameSection count];
}
- (UITableViewCell *) extracted_method: (UITableViewCell *) cell {
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)aTableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
static NSString *sectionsTableIdentifier = #"sectionsTableIdentifier";
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:
sectionsTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: sectionsTableIdentifier] autorelease];
}
cell.backgroundColor = [UIColor clearColor];
cell.textColor = [UIColor whiteColor];
cell.detailTextLabel.textColor = [UIColor whiteColor];
cell.text = [nameSection objectAtIndex:row];
[self extracted_method: cell].text = [listData objectAtIndex:row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
if ([keys count] == 0)
return nil;
NSString *key = [keys objectAtIndex:section];
return key;
}
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return keys;
}
#pragma mark -
#pragma mark Table View Delegate Methods
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[search resignFirstResponder];
return indexPath;
}
#pragma mark -
#pragma mark Search Bar Delegate Methods
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSString *searchTerm = [searchBar text];
[self handleSearchForTerm:searchTerm];
}
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchTerm {
if ([searchTerm length] == 0) {
[self resetSearch];
[table reloadData];
return;
}
[self handleSearchForTerm:searchTerm];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
search.text = #"";
[self resetSearch];
[table reloadData];
[searchBar resignFirstResponder];
}
#end
Ok guys. My problem is that this doesnt get the search function to work. In addition I receive siginal SIGABRT at this line:
NSString *key = [keys objectAtIndex:section];
So I need help with two things:
1: I need to get that SIGABRT away.
Error log message: * Terminating app due to uncaught exception
'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]:
index 0 beyond bounds for empty array'
That is I don't store any data in keys. how would I do that? and what would I store?
2: Want the search function to search in my listData array!
Thanks in advance - hope u can help!
You have not finished your sectionIndexTitlesForTableView: method. Right now it is:
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return keys;
There is no closing }, so the compiler thinks everything after that is still part of that method. When you try to define the next method, use use - (NSIndexPath *) to indicate that it is an instance method which returns NSIndexPath*, but the compiler thinks you are trying to subtract something.
The solution is simple: Add the } to the end of sectionIndexTitlesForTableView:.
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return keys;
}