Im working on a splitview app , where the master view is a table view fetched from sqlite database ,when the user tab a row the detail view UITableView get updated by data also fetched from the same sqlite database.
Im able to populate the master view table view , but cann`t get the detail view table view to update with the new data?
I searched everywhere on how to do this but no avail , most tutorials and answer are about label or webview, but neverfound something about detailview table view.
I`m using stroyboard and xcode 4.5.2
here is the masterviewcontroller.m didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Destinations *dest = [self.destinations objectAtIndex:[indexPath row]];
self.detailViewController.title = dest.destName;
self.detailViewController.detailItem = dest.destID;
[self.tableView reloadData];
}
And here is the detailviewcontroller.h
#import <UIKit/UIKit.h>
#import "FMDatabase.h"
#interface DetailViewController : UIViewController<UITableViewDelegate, UITableViewDataSource,UISplitViewControllerDelegate>
{
NSMutableArray *alternates;
UITableView *alternateTableView;
}
#property (nonatomic,strong) NSMutableArray *alternates;
#property (strong, nonatomic) id detailItem;
#property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
#property (strong, nonatomic) IBOutlet UITableView *alternateTableView;
#property (retain, nonatomic) NSString *pdestid;
#end
The detailviewcontroller.m
#import "DetailViewController.h"
#import "Alternates.h"
#interface DetailViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
#end
#implementation DetailViewController
#synthesize pdestid, alternates,alternateTableView;
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
self.pdestid = [self.detailItem description];
NSFileManager * fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex: 0];
NSString * writableDBPath = [documentsDirectory stringByAppendingPathComponent: #"alternates.s3db"];
BOOL success = [fileManager fileExistsAtPath: writableDBPath];
if (! success) {
NSString * defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: #"alternates.s3db"];
success = [fileManager copyItemAtPath: defaultDBPath toPath: writableDBPath error: & error];
}
FMDatabase * db = [FMDatabase databaseWithPath: writableDBPath];
if (! [db open])
{
NSLog (#"Err% d:%#", [db lastErrorCode], [db lastErrorMessage]);
}
[db setShouldCacheStatements: YES];
NSString * sql = [NSString stringWithFormat:#"SELECT * FROM Alternates WHERE destID = '%#' ORDER BY altName ASC;", pdestid];
FMResultSet * rs = [db executeQuery: sql];
alternates = [[NSMutableArray alloc] init];
while ([rs next])
{
Alternates * alter = [[Alternates alloc] init];
alter.altname = [rs stringForColumn: #"altName"];
alter.alticao = [rs stringForColumn: #"altICAO"];
alter.dist = [rs stringForColumn:#"distance"];
alter.mgtrk =[rs stringForColumn:#"magtrack"];
alter.route = [rs stringForColumn:#"route"];
[alternates addObject:alter];
}
[rs close];
[db close];
}
}
- (void)viewDidLoad
{
// Do any additional setup after loading the view, typically from a nib.
[super viewDidLoad];
[self configureView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return alternates.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"AlternateCell"];
Alternates *alter = [self.alternates objectAtIndex:[indexPath row]];
cell.textLabel.text = alter.altname;
return cell;
}
#pragma mark - Split view
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = NSLocalizedString(#"DESTINATIONS", #"DESTINATIONS");
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
- (void)viewDidUnload {
[self setAlternateTableView:nil];
[super viewDidUnload];
}
#end
In your DetailViewController are you reloading your table view after getting the data from the database in configureView?
Try adding a reloadData on your table view:
- (void) configureView
{
// [... as shown above ...]
[self.alternateTableView reloadData];
}
If your detail is a tableviewController you may want to add a [self.tableView reloadData] in configureView
Related
I am creating an iOS app. Since I am a beginner, I started with checking out some tutorials.
I started with creating an app with a Table View. The cells are dynamically filled through a database connection. This works fine.
However now I'm trying to define a push segue that opens a detail view for the cells. These also have to become filled dynamically with data. To achieve this, I started going through this tutorial and got it working up to "Passing Data Using Segue". In that step, you have to assign an identifier to your segue and fill in the prepareForSegue:sender: method.
I believe the implementation of my method causes the above error, because it isn't doing what it's supposed to. Looking up the error did not provide me with (understandable) answers since I fail to see how this problem has arisen.
Could someone please take a look at my code? Thanks in advance.
Main.storyboard:
In case you might be wondering, I did add an identifier to my segue, named showEventDetails.
EventsTableViewController.h
#import <UIKit/UIKit.h>
#import <sqlite3.h>
#interface EventsTableViewController : UITableViewController {
NSMutableArray *EventsArray;
sqlite3 *db;
}
#property (nonatomic, retain) NSMutableArray *EventsArray;
#property (nonatomic, strong) IBOutlet UITableView *eventTable;
-(NSMutableArray *) EventList;
#end
EventsTableViewController.m
#import "EventsTableViewController.h"
#import "TableCellViewController.h"
#import "Event.h"
#import <sqlite3.h>
#implementation EventsTableViewController
#synthesize EventsArray;
#synthesize eventTable;
- (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.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[self EventList];
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (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.
return [self.EventsArray count];
}
- (NSMutableArray *) EventList
{
EventsArray = [[NSMutableArray alloc] initWithCapacity:10];
#try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"eventsDb.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success) {
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if (!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK)) {
NSLog(#"An error has occured: %s", sqlite3_errmsg(db));
}
const char *sql = "SELECT * FROM events";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK) {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(db));
}
else {
while (sqlite3_step(sqlStatement) == SQLITE_ROW) {
Event *event = [[Event alloc] init];
event.name = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 1)];
event.date = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
event.starttime = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 3)];
event.endtime = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 4)];
event.location = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 5)];
event.description = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 6)];
event.favourite = sqlite3_column_int(sqlStatement, 7);
[EventsArray addObject:event];
event = nil;
}
}
sqlite3_finalize(sqlStatement);
}
#catch (NSException *exception) {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(db));
}
#finally {
sqlite3_close(db);
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"EventCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
int rowCount = indexPath.row;
Event *event = [self.EventsArray objectAtIndex:rowCount];
cell.textLabel.text = event.name;
cell.detailTextLabel.text= event.description;
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showEventDetails"]) {
NSIndexPath *indexPath = [self.eventTable indexPathForSelectedRow];
TableCellViewController *destViewController = segue.destinationViewController;
destViewController.eventName = [EventsArray objectAtIndex:indexPath.row];
}
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
#end
TableCellViewController.h
#import <UIKit/UIKit.h>
#interface TableCellViewController : UIViewController
#property(nonatomic, copy) NSString *eventName;
#property(nonatomic, strong) IBOutlet UILabel * eventNameLabel;
#property(nonatomic, copy) NSString *eventDate;
#property(nonatomic, strong) IBOutlet UILabel * eventDateLabel;
#property(nonatomic, copy) NSString *eventStarttime;
#property(nonatomic, strong) IBOutlet UILabel * eventStarttimeLabel;
#property(nonatomic, copy) NSString *eventEndtime;
#property(nonatomic, strong) IBOutlet UILabel * eventEndtimeLabel;
#property(nonatomic, copy) NSString *eventLocation;
#property(nonatomic, strong) IBOutlet UILabel * eventLocationLabel;
#property(nonatomic, copy) NSString *eventDescription;
#property(nonatomic, strong) IBOutlet UILabel * eventDescriptionLabel;
#property(nonatomic, assign) NSInteger eventFavourite;
#property(nonatomic, strong) IBOutlet UILabel * eventFavouriteLabel;
#end
TableCellViewController.m
#import "TableCellViewController.h"
#interface TableCellViewController ()
#end
#implementation TableCellViewController
#synthesize eventName;
#synthesize eventNameLabel;
#synthesize eventDate;
#synthesize eventDateLabel;
#synthesize eventStarttime;
#synthesize eventStarttimeLabel;
#synthesize eventEndtime;
#synthesize eventEndtimeLabel;
#synthesize eventLocation;
#synthesize eventLocationLabel;
#synthesize eventDescription;
#synthesize eventDescriptionLabel;
#synthesize eventFavourite;
#synthesize eventFavouriteLabel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Set the eventNameLabel with the name of the event
eventNameLabel.text = eventName;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
Thanks again!
This is the offending line:
destViewController.eventName = [EventsArray objectAtIndex:indexPath.row];
You're trying to assign an object of type Event stored in the array, to an object of type NSString.
Since your string is defined with the copy modifier, the assignment tries to copy to object on the right side of the assignment (Event) but apparently this object doesn't conform to the NSCopying protocol, hence the error unrecognized selector... copyWithZone....
I'm struggling to populate a Table View using JSON Data from Youtube (V 2.1) which has been parsed(Logged the output in the console)
Every time I am loading the Table View Controller, nothing is populated. I have even created a 'Video' class (NSObject). I'm struggling to understand what I'm doing wrong.
The following is my code:
Video.h
#import <Foundation/Foundation.h>
#interface Video : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *description;
#property (nonatomic, strong) NSString *thumbnail;
#property (nonatomic, strong) NSString *uploadedDate;
#property (nonatomic, strong) NSURL *url;
// Designated Initializer
- (id) initWithTitle:(NSString *)title;
+ (id) videoWithTitle:(NSString *)title;
- (NSURL *) thumbnailURL;
- (NSString *) formattedDate;
#end
Video.m
import "Video.h"
#implementation Video
- (id) initWithTitle:(NSString *)title {
self = [super init];
if ( self ){
self.title = title;
self.thumbnail = nil;
}
return self;
}
+ (id) videoWithTitle:(NSString *)title {
return [[self alloc] initWithTitle:title];
}
- (NSURL *) thumbnailURL {
// NSLog(#"%#",[self.thumbnail class]);
return [NSURL URLWithString:self.thumbnail];
}
- (NSString *) formattedDate {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *tempDate = [dateFormatter dateFromString:self.uploadedDate];
[dateFormatter setDateFormat:#"EE MMM,dd"];
return [dateFormatter stringFromDate:tempDate];
}
#end
Table View Controller implementation file (the one I'm trying to populate)
#import "FilmyViewController.h"
#import "Video.h"
#interface FilmyViewController ()
#end
#implementation FilmyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *videoURL = [NSURL URLWithString:#"http://gdata.youtube.com/feeds/api/users/OrtoForum/uploads?v=2&alt=jsonc"];
NSData *jsonData = [NSData dataWithContentsOfURL:videoURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSLog(#"%#",dataDictionary);
self.videoArray = [NSMutableArray array];
NSArray *videosArray = [dataDictionary objectForKey:#"items"];
for (NSDictionary *vDictionary in videosArray) {
Video *video = [Video videoWithTitle:[vDictionary objectForKey:#"title"]];
video.title = [vDictionary objectForKey:#"title"];
video.description = [vDictionary objectForKey:#"author"];
video.uploadedDate = [vDictionary objectForKey:#"uploaded"];
video.url = [NSURL URLWithString:[vDictionary objectForKey:#"url"]];
[self.videoArray addObject:video];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#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.
return [self.videoArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Video *video = [self.videoArray objectAtIndex:indexPath.row];
// Configure the cell...
cell.textLabel.text = video.title;
cell.textLabel.text = video.description;
return cell;
}
/*
#pragma mark - Navigation
// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
Here's the JSON which I'm trying to extract from.
I looked for similar topics but didn't get any appropriate solution for this.
Research Link-one and Link-two is what i have been trying to follow.
Please let me know if there is any better approach for this.
What am i missing here?
Solution
Changed
NSArray *videosArray = [dataDictionary objectForKey:#"items"];
to:
NSArray *videosArray = dataDictionary[#"data"][#"items"];
Change
NSArray *videosArray = [dataDictionary objectForKey:#"items"];
to
NSArray *videosArray = dataDictionary[#"data"][#"items"];
Your items array is in the second level: rootJSON -> data -> items
I have a view that is inserting information into the database. This view displays that information inside a table view (top half of view is being used for something else)
I have the view controller's class, an object class, and a table view cell class (this is what's throwing me off I think because the image and text labels are in here whereas the actual database information is in the view controller).
Here's the NSObject class:
// car.h
#import <Foundation/Foundation.h>
#interface Car : NSObject {
NSString *displayedMake;
NSString *displayedModel;
NSString *displayedImage;
}
#property (nonatomic, strong) NSString *displayedMake;
#property (nonatomic, strong) NSString *displayedModel;
#property (nonatomic, strong) NSString *displayedImage;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u;
#end
//car.m
#import "Car.h"
#implementation Car
#synthesize displayedMake, displayedModel, displayedImage;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
self.displayedMake = n;
self.displayedModel = d;
self.displayedImage = u;
return self;
}
#end
Here's the table view cell class:
//DisplayCarsCell.h
#import <UIKit/UIKit.h>
#interface DisplayCarsCell : UITableViewCell
#property (nonatomic, strong) IBOutlet UIImageView *carImage;
#property (nonatomic, strong) IBOutlet UILabel *makeLabel;
#property (nonatomic, strong) IBOutlet UILabel *modelLabel;
#end
//DisplayCarsCell.m
#import "DisplayCarsCell.h"
#import "ViewController.h"
#implementation DisplayCarsCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
And finally, the view controller:
//ViewController.h
#import <UIKit/UIKit.h>
#import "/usr/include/sqlite3.h"
#interface ViewController : UIViewController <UITableViewDelegate> {
//NSString *databasePath;
sqlite3 *carsDB;
NSArray *carImages;
NSMutableArray *cars;
}
#property (nonatomic, retain) NSMutableArray *cars;
#property (strong, nonatomic) IBOutlet UILabel *status;
#end
//ViewController.m
#import "ViewController.h"
#import "DisplayCarsCell.h"
#import "Car.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize cars;
-(NSString *) filePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[paths objectAtIndex:0] stringByAppendingPathComponent:#"cars.db"];
}
-(void) openDB {
if (sqlite3_open([[self filePath]UTF8String],&carsDB) != SQLITE_OK){
sqlite3_close(carsDB);
NSAssert(0, #"Database failed to open");
}
else{
NSLog(#"database opened");
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Opens the database and creates the table if its the first time
[self readCarsFromDatabase];
}
- (void)readCarsFromDatabase
{
// Setup the database object
//sqlite3 *database;
// Init the animals Array
cars = [[NSMutableArray alloc] init];
// Open the database from the users filessytem
[self openDB];
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS apps (name text, id text, image text)";
if (sqlite3_exec(carsDB, sql_stmt, NULL, NULL, NULL) == SQLITE_OK)
{
NSLog(#"create db");
}
//Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from apps";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(carsDB, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
//image, or first column in table
NSString *displayedImage=[NSString stringWithFormat: #"%s",(const char *)sqlite3_column_text(compiledStatement, 0)];
//name, or second column in table
NSString *displayedMake=[NSString stringWithFormat: #"%s",(const char *)sqlite3_column_text(compiledStatement, 1)];
//model, or third column in table
NSString *displayedModel=[NSString stringWithFormat: #"%s",(const char *)sqlite3_column_text(compiledStatement, 2)];
NSLog(#"%#, %#, %#",displayedImage, displayedMake, displayedModel);
_status.text = displayedMake;
NSLog(#"here");
// Create a new animal object with the data from the database
Car *carObject = [Car alloc];
carObject.displayedImage = [NSString stringWithFormat: #"%s",(const char *)sqlite3_column_text(compiledStatement, 0)];
// Add the animal object to the animals Array
[cars addObject:carObject];
//NSLog(#"%#",displayedImage);
}
}
else
{
printf( "could not prepare statement: %s\n", sqlite3_errmsg(carsDB) );
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
sqlite3_close(carsDB);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
#pragma mark Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return carImages.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DisplayCarsCell";
DisplayCarsCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[DisplayCarsCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Configure the cell...
//UIImage *displayedImage = [UIImage imageNamed:
//[carImage objectAtIndex: [indexPath row]]];
// Car *carObj = [cars objectAtIndex:indexPath.row];
ViewController *viewController = (ViewController *)[[UIApplication sharedApplication] delegate];
Car *car = (Car *)[viewController.cars objectAtIndex:indexPath.row];
//[cell setImage:car.displayedImage];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// TODO: Select Item
NSLog(#"Selected");}
#end
The view controller itself is getting the array of data obviously from readCarsFromDatabase and is logging each row of the database correctly on viewDidLoad (did this to make sure that part wasn't wrong), but I need it to update the rows of the table view that is inside.
You have to call reloadData for your tableview once you're done adding records to cars, for example at the end of readCarsFromDatabase. I don't see an IBOutlet for the UITableView, but if it was simply tableView, then you'd add a line after sqlite3_close that says:
[self.tableView reloadData];
Obviously, make sure the table view's delegate is set to be your view controller.
Also, I would have thought that the following lines from your view controller's cellForRowAtIndexPath that currently say:
ViewController *viewController = (ViewController *)[[UIApplication sharedApplication] delegate];
Car *car = (Car *)[viewController.cars objectAtIndex:indexPath.row];
should be changed to simply say:
Car *car = self.cars[indexPath.row];
I'm getting a EXC_BAD_ACCESS crash when switching back and forth between views. I'm having a problem finding the cause of this crash. In the simulator it always goes back to the main.m file and reports the crash in it.
But on my device the EXC_BAD_ACCESS show up on my custom UITableViewCell when I release it in the dealloc method. If I enable NSZombieEnabled my app doesn't crash at all.
Here is the .h file
#import <UIKit/UIKit.h>
#define kWinsAmountTagValue 2 // how many wins you have
#define kWinningsAmountTagValue 3 // how much money you won
#interface MyStatsViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource,
UINavigationBarDelegate, UINavigationControllerDelegate>{
NSArray *list;
UITableView *theTable;
UITableViewCell *theCell;
}
#property (nonatomic, retain) NSArray *list;
#property (nonatomic, retain) IBOutlet UITableView *theTable;
#property (nonatomic, retain) IBOutlet UITableViewCell *theCell;
// dealloc and cleanup
-(void) dealloc;
// misc methods
-(void)loadData;
// demo data
-(NSArray *)tableData;
#end
Here is my .m file
#import "MyStatsViewController.h"
#implementation MyStatsViewController
#synthesize list;
#synthesize theTable;
#synthesize theCell;
#pragma mark - dealloc and cleanup
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
NSLog(#"Memory Warning");
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.list = nil;
self.theTable = nil;
self.theCell = nil;
}
- (void)dealloc
{
[super dealloc];
[list release];
[theTable release];
[theCell release];
}
#pragma mark - misc methods
-(void) loadData
{
self.list = [self tableData];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(void)viewWillAppear:(BOOL)animated
{
[self loadData];
[theTable reloadData];
}
#pragma mark - Table Data Source Methods
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [list count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier =#"MyStatsCustomCellIdentifer";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
NSUInteger row = [indexPath row];
if (cell == nil) {
if (row == [list count] -1) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
} else {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyStatsCustomCell"
owner:self
options:nil];
if ([nib count] > 0) {
cell = self.theCell;
} else {
NSLog(#"failed to load MyStatsCustomCell");
}
}
}
// Add custom stuff here for rows
//cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (row == [list count] -1) {
cell.textLabel.text = [list objectAtIndex:row];
} else {
UILabel *prizeLevel = (UILabel *)[cell viewWithTag:kPrizeLevelTagValue];
prizeLevel.text = [[list objectAtIndex:row] objectForKey:#"prizeLevel"];
UILabel *winsAmount = (UILabel *)[cell viewWithTag:kWinsAmountTagValue];
winsAmount.text = [[list objectAtIndex:row] objectForKey:#"winsAmount"];
UILabel *winningsAmount = (UILabel *)[cell viewWithTag:kWinningsAmountTagValue];
winningsAmount.text = [[list objectAtIndex:row] objectForKey:#"winningsAmount"];
}
//NSLog(#"theCell Retain: %i",[theCell retainCount]);
return cell;
}
#pragma mark - Table View Delegate Methods
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - demo data
-(NSArray *)tableData
{
NSArray *prizeLevels = [[NSArray alloc] initWithObjects:
#"6-of-6", #"5-of-6", #"4-of-6",#"3-of-6", nil];
NSArray *winsAmount = [[NSArray alloc] initWithObjects:
#"0", #"0", #"2", #"100", nil];
NSArray *winngingsAmount = [[NSArray alloc] initWithObjects:
#"$0",#"$0", #"$45.50",#"$125.00", nil];
NSMutableArray *myGames = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [prizeLevels count]; i++) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[prizeLevels objectAtIndex:i] forKey:#"prizeLevel"];
[dict setObject:[winsAmount objectAtIndex:i] forKey:#"winsAmount"];
[dict setObject:[winngingsAmount objectAtIndex:i] forKey:#"winningsAmount"];
[myGames addObject:dict];
[dict release];
}
[prizeLevels release];
[winsAmount release];
[winngingsAmount release];
[myGames addObject:#"Spent: $1250.00"];
return myGames;
}
#end
Any help would be appreciated.
It is a good practice to clean up class's own variables before calling the super's destructor. A lot more details can be found here: Why do I have to call super -dealloc last, and not first?.
Ok, here is my code:
(appname)AppDelegate.h:
#import <UIKit/UIKit.h>
#class TwitterViewContoller;
#interface <appname>AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UITabBarController *rootController;
TwitterViewContoller *viewController;
NSMutableData *responseData;
NSMutableArray *tweets;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *rootController;
#property (nonatomic, retain) IBOutlet TwitterViewContoller *viewController;
#property (nonatomic, retain) NSMutableArray *tweets;
#end
(appname)AppDelegate.m:
#import "<appname>AppDelegate.h"
#import "TwitterViewContoller.h"
#import "SBJson.h"
#implementation <appname>AppDelegate
#synthesize window = _window;
#synthesize rootController;
#synthesize viewController;
#synthesize tweets;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
responseData = [[NSMutableData data] retain];
tweets = [NSMutableArray array];
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://api.twitter.com/1/statuses/user_timeline.json?screen_name=USER_NAME_ID"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
//[window addSubview:rootController.view];
//[window makeKeyAndVisible];
return YES;
}
#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 {
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
//NSDictionary *results = [responseString JSONValue]; <---This...
//NSArray *allTweets = [results objectForKey:#"results"]; <--- and this was original code but it caused an exception,
NSArray *allTweets = [responseString JSONValue]; //<-- So I used this instead.
NSLog(#"THE ARRAY = %#", allTweets); //<-- THE ARRAY IS SHOWING FINE IN THE OUTPUT LOG
[viewController setTweets:allTweets];
[self.window addSubview:rootController.view];
[self.window makeKeyAndVisible];
}
- (void)dealloc {
[_window release];
[rootController release];
[viewController release];
[super dealloc];
}
#end
TwitterViewContoller.h: (yes, I know I spelt it wrong - lol)
#import <UIKit/UIKit.h>
#import "SBJson.h"
#interface TwitterViewContoller : UIViewController {
IBOutlet UILabel *label;
NSArray *tweets;
IBOutlet UITableView *tview;
}
#property(nonatomic, retain) IBOutlet UILabel *label;
#property(nonatomic, retain) NSArray *tweets;
#end
TwitterViewContoller.m:
#import "TwitterViewContoller.h"
#import "Tweet.h"
#implementation TwitterViewContoller
#synthesize label;
#synthesize tweets;
#pragma mark -
#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.
return 20;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 80;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
NSLog(#"THE ARRAY = %#", tweets); //<--ARRAY IS NULL <-- <-- <--
NSDictionary *aTweet = [tweets objectAtIndex:[indexPath row]];
cell.textLabel.text = [aTweet objectForKey:#"text"];
cell.textLabel.adjustsFontSizeToFitWidth = YES;
cell.textLabel.font = [UIFont systemFontOfSize:12];
cell.textLabel.minimumFontSize = 10;
cell.textLabel.numberOfLines = 4;
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.detailTextLabel.text = [aTweet objectForKey:#"screen_name"];
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;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
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.
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
tweets = [[NSArray alloc]init];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void) dealloc {
[tweets release];
[super dealloc];
}
#end
The problem is that my UITableView is showing up but none of my tweets are. I have noticed that the "tweets" array in TwitterViewContoller (I know I spelt it wrong) is empty (the null problem is fixed) but "allTweets" in my AppDelegate isn't empty. Any ideas?
Test to ensure that in your app delegate, the viewController property is not nil. If you have miswired it in IB (I don't see any instantiation of it in your app delegate), it will silently fail when you call setTweets.
Also, if your view controller is visible and you re-set the tweets property, the table view won't update itself. Instead of synthesizing your tweets property, write your own getter and setter, like so (you could also use KVO for this purpose if you like)
-(NSMutableArray*)tweets {
return [[tweets retain] autorelease];
}
-(void)setTweets:(NSMutableArray*)newTweets {
if(newTweets != tweets) {
[newTweets retain];
[tweets release];
tweets = newTweets;
[tView reloadData];
}
}
As an aside, you should have -tableView:numberOfRowsInSection: return [tweets count] rather than a fixed number, and it isn't necessary to implement -tableView:heightForRowAtIndexPath: if your rows are all the same height -- you can just set the rowHeight property in IB or in code on the table view.
Update
If your view controller property isn't getting populated, it might be easiest to just load and add it manually in -application:didFinishLaunchingWithOptions -- I think your current approach of delaying the display of any interface until the web request finishes is conceptually problematic. What happens if the network isn't available? Here's some code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ... omitting the first few lines
// let's make sure that we have the tab bar controller, at least
NSAssert(nil != self.rootViewController, #"tab bar controller not hooked up!");
self.viewController = [[[TwitterViewContoller alloc] initWithNibName:#"TwitterViewContoller" andBundle:nil] autorelease];
self.rootViewController.viewControllers = [NSArray arrayWithObject:self.viewController];
// this is now the Right Way to set the root view for your app, in iOS 4.0 and later
self.window.rootViewController = self.rootViewController;
[self.window makeKeyAndVisible];
}
There are a lot of ways that you can confuse yourself when you're starting a new project. I like to customize all of my views to have hideous background colors at first so I always know if they're on screen or not -- using ugly colors helps ensure that I don't forget to change them later.