I have encountered a problem of placing data into the Table View. Here is my current code.
I am unable to load the results from the class I tried to use NSFetchedResultsController, but it won't work. Can any one see the mistake.
This is the header file.
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import <sqlite3.h>
#import "FlickrFetcher.h"
#interface PersonList : UITableViewController {
FlickrFetcher *fetcher;
NSArray *nameList;
NSArray *names;
NSFetchedResultsController *results;
NSArray *photos;
}
#property (retain, nonatomic)NSArray *nameList;
#property (retain, nonatomic)NSArray *names;
#property (retain, nonatomic)NSArray *photos;
#property (retain, nonatomic)NSFetchedResultsController *results;
#end
This is the .m file.
#import "PersonList.h"
#implementation PersonList
#synthesize nameList,names,results,photos;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
self.title=#"Contacts";
// Custom initialization
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
fetcher = [FlickrFetcher sharedInstance];
NSString *path=[[NSBundle mainBundle]pathForResource:#"FakeData" ofType:#"plist"];
NSArray *array=[NSArray arrayWithContentsOfFile:path];
NSManagedObjectContext *context=[fetcher managedObjectContext];
if([fetcher databaseExists]==YES){
for(NSDictionary *dic in array){
PersonList *person=(PersonList *)[NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
[person setNameList:[dic objectForKey:#"user"]];
[person setPhotos:[dic objectForKey:#"path"]];
names=[fetcher fetchManagedObjectsForEntity:#"Person" withPredicate:nil];
results=[fetcher fetchedResultsControllerForEntity:#"Person" withPredicate:nil];
}
}
[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.names=nil;
}
- (void)dealloc {
[fetcher release];
[nameList release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[results sections] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"NOT REACHED HERE");
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SimpleTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [[results sections] objectAtIndex:row];
return cell;
}
#end
This the method for the FlickrFetcher method
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface FlickrFetcher : NSObject {
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
// Returns the 'singleton' instance of this class
+ (id)sharedInstance;
// Checks to see if any database exists on disk
- (BOOL)databaseExists;
// Returns the NSManagedObjectContext for inserting and fetching objects into the store
- (NSManagedObjectContext *)managedObjectContext;
// Returns an array of objects already in the database for the given Entity Name and Predicate
- (NSArray *)fetchManagedObjectsForEntity:(NSString*)entityName withPredicate:(NSPredicate*)predicate;
// Returns an NSFetchedResultsController for a given Entity Name and Predicate
- (NSFetchedResultsController *)fetchedResultsControllerForEntity:(NSString*)entityName withPredicate:(NSPredicate*)predicate;
#end
Put your call to [super viewDidLoad] before your code. The only time a call to super should be after your code is in the -dealloc method.
You are looping over array, getting a NSFetchedResultsController and doing nothing with it. You are not even keeping a reference. This is incorrect. You should have ONE NSFetchedResultsController per UITableViewController; you should retain reference to it; your UITableViewDatasource (which in this case is your UITableViewController) should be its delegate.
You are not calling -performFetch: on the NSFetchedResultsController so there is no data being retrieved.
Because you are not the delegate of the NSFetchedResultsController you would have no way of knowing when it got data back anyway because that is the singular way that it communicates data changes.
I would reconsider your design and review the Core Data iPhone sample projects again.
Update
The NSFetchedResultsController does not fire its delegate methods until you save the NSManagedObjectContext so that is why you are not seeing data.
As for the error you are getting, you are trying to set a property on an object that does not respond to that property. I would suggest loading your application into the debugger, putting a breakpoint on objc_exception_throw and see what object you are manipulating that is causing the problem. Most likely you are getting back one object and thinking it is another.
Related
I made a simple project to explain my problem. Basically what I did was make a project with the templet of a Master Detail Application. Then in the
DetailViewController.h
nothing
DetailViewController.m
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"showDetail"])
{
[segue.destinationViewController setCellName2:#"New String"];
}
}
MasterViewController.h
#property(nonatomic,strong) NSString *cellName2;
MasterViewController.m
#synthesize cellName2;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(#"%#", cellName2);
}
My problem is in the detailViewController where I set the cellName2, I cant set it because the detailViewController is the receivingViewController of the segue. Is their a method to set the sendingViewController end of a segue?
Edit
After Firo's answer my code looks like this now
MasterViewContoller.h
#import <UIKit/UIKit.h>
#import "DetailViewController.h"
#interface MasterViewController : UITableViewController <DetailViewDelegate>
#property(nonatomic,strong) NSString *cellName2;
#end
.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#interface MasterViewController () {
NSMutableArray *_objects;
}
#end
#implementation MasterViewController
#synthesize cellName2;
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
self.cellName2 = cellName;
NSLog(#"%#", self.cellName2);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (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:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSDate *object = _objects[indexPath.row];
cell.textLabel.text = [object description];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_objects removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
/*
// 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;
}
*/
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"showDetail"]) {
/* assigning self as delegate, telling the detail view that I implement
* setCellName2:, so it (the detailVC) can call it whenever it wants to.
*/
[segue.destinationViewController setDelegate:self];
}
}
// my implementation of the DetailViewDelegate protocol that I abide to
/* note: #pragma mark is not required, just for comment, documentation
* and find-ability purposes
*/
//#pragma mark - DetailViewDelegate
// note: this is just a property setter so this is not actually needed
//- (void)setCellName2:(NSString *)cellName {
// self.cellName2 = cellName;
// NSLog(#"%#", self.cellName2);
//}
#end
DetailViewContoller.h
#import <UIKit/UIKit.h>
/* defining a protocol, whoever is a DetailViewDelegate must implement my
* defined methods
*/
#protocol DetailViewDelegate <NSObject>
- (void)setCellName2:(NSString *)cellName;
#end
#interface DetailViewController : UIViewController
/* storing a delegate property. Whoever sets themselves to my delegate
* must implement my DetailViewDelegate's methods (setCellName2: in this case)
*/
#property (weak, nonatomic) id<DetailViewDelegate> delegate;
#property (strong, nonatomic) id detailItem;
#property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
#end
.m
#import "DetailViewController.h"
#interface DetailViewController ()
- (void)configureView;
#end
#implementation DetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//[self.delegate setCellName2:#""];
[self configureView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"showDetaila"])
{
[segue.destinationViewController setCellName2:#"New String"];
}
}
#end
The problem now is, i am getting the error "Use of undeclared identifier 'cellName'; did you mean 'cellName2'?"
You are looking for delegates and protocols. Since you are not segueing from your detailVC to your masterVC you cannot put this setter in prepareForSegue. You need to store a reference to the master and have callbacks to it. Here is how you would do it with your basic example:
DetailViewController.h
/* defining a protocol, whoever is a DetailViewDelegate must implement my
* defined methods
*/
#protocol DetailViewDelegate <NSObject>
- (void)setCellName2:(NSString *)cellName;
#end
#interface DetailViewController : UIViewController
/* storing a delegate property. Whoever sets themselves to my delegate
* must implement my DetailViewDelegate's methods (setCellName2: in this case)
*/
#property (weak, nonatomic) id<DetailViewDelegate> delegate;
#end
DetailViewController.m
#implementation DetailViewController
// some action or method
- (IBAction)buttonPress:(id)sender {
// look below at Master's prepareForSegue
/* calling the method that my delegate implements, my delegate can be any
* object that implements my protocol (DetailViewDelegate)
*/
[self.delegate setCellName2:#""];
}
#end
MasterViewController.h
#import "DetailViewController.h"
// saying I implement the DetailViewDelegate protocol (and all necessary methods)
#interface MastViewController : UITableViewController <DetailViewDelegate>
#end
MasterViewController.m
#interface MasterViewController ()
#property(strong, nonatomic) NSString *cellName2;
#end
#implementation MasterViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"showDetail"]) {
/* assigning self as delegate, telling the detail view that I implement
* setCellName2:, so it (the detailVC) can call it whenever it wants to.
*/
[segue.destinationViewController setDelegate:self];
}
}
// my implementation of the DetailViewDelegate protocol that I abide to
/* note: #pragma mark is not required, just for comment, documentation
* and find-ability purposes
*/
#pragma mark - DetailViewDelegate
// note: this is just a property setter so this is not actually needed
- (void)setCellName2:(NSString *)cellName {
_cellName = cellName
NSLog("%#", self.cellName);
}
#end
I would give you some more information about delegates and protocols but it is an extremely common pattern when dealing with iOS development. You also should become extremely familiar with delegates and protocols, you will find it useful in many situations and it will help you better understand iOS development and make you a more competent programmer. If something does not work (or make sense) let me know. I just typed this up in SO so there could be some mild mistakes.
Edit
Note: If this becomes to far from the original question you may need to just create a new one.
Your main issue here is that you have self.cellName2 = cellName; in your MasterViewController. According to your original post you want the DetailVC to set this, right? So it will need to go into DetailViewController's viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[self.delegate setCellName2:#"My custom text!"];
}
Then remove:
self.cellName2 = cellName;
NSLog(#"%#", self.cellName2);
From your MasterViewController's viewDidLoad. Your error is with the first line there (I am assuming). What is cellName? It is not a string and you have not defined it as a variable or property, hence the error.
Are you sure that the code is in the right class? And that you have the segues hooked up correctly in the storyboard?
In your DetailViewController code, you indicate that the segue identifier is "showDetail". It would seem to me that you ought to be handling that in your MasterViewController, as the pattern is typically that MasterViewController is responsible for seguing to the DetailViewController for the purpose of showing details.
Basically, if the destinationViewController property of the segue is NOT an instance of MasterViewController, then either the segue is not correctly configured or your code is in the wrong place.
If I'm mistaken please post more code.
First of I would like to start with I am 100% new to iPhone development. I believe this is a quite simple question for someone experienced. Anyhow, what I am trying to do is:
The user should via the SearchUI be able to search for objects from my database. If the object exist, display it in the tableView where the search-objects will be displayed. I manage to get the objects from the database but not instance them into the tableview and display them.
Honestly I don't know what I am doing wrong. All help will be really appreciated and also some explaining if possible. Under method - (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects - I try to move the objects into the tableView without any success. You find the method in FirstViewController.m at the end of pragma mark TableView Data Scource methods. Here is my code:
FirstViewController.h class
#import <UIKit/UIKit.h>
#import <RestKit/RestKit.h>
#interface FirstViewController : UIViewController <UITableViewDataSource, RKObjectLoaderDelegate>
{
UISearchDisplayController *searchDisplayController;
UISearchDisplayController *searchBar;
UITableView *table;
NSArray *allItems;
NSArray *searchResults;
}
#property (nonatomic, retain) IBOutlet UISearchDisplayController *searchDisplayController;
#property (nonatomic, retain) IBOutlet UISearchDisplayController *searchBar;
#property (nonatomic, retain) IBOutlet UITableView *table;
#property (nonatomic, copy) NSArray *allItems;
#property (nonatomic, copy) NSArray *searchResults;
#end
FirstViewController.m class
#import "FirstViewController.h"
#import "Task.h"
interface FirstViewController ()
end
implementation FirstViewController
#synthesize searchDisplayController;
#synthesize searchBar;
#synthesize allItems;
#synthesize searchResults;
#synthesize table;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
//self.listContent = [[NSArray alloc] initWithObjects:#"John", #"Paul", nil];
//self.filteredListContent = [NSMutableArray arrayWithCapacity:10];
[super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
else
{
return YES;
}
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
return NO;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
return NO;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)pSearchBar
{
[[RKObjectManager sharedManager].mappingProvider setMapping:[Task getMapping] forKeyPath:#"tasks"];
NSString *path = [NSString stringWithFormat:#"%#/%#/%#", #"/book/1/tasks/", pSearchBar.text, #".json"];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:path delegate:self];
NSLog(#"Search: %#", pSearchBar.text);
}
#pragma mark - TableView Data Scource methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.searchResults count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"];
}
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
return cell;
}
- (void) deselect {
//[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
// Respond to user selection tap by coloring the navigation bar
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath
{
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects
{
self.searchResults = objects;
[self.table reloadData];
for(Task *task in objects)
{
if ([task isKindOfClass:[Task class]])
{
NSLog(#"Loaded Book ID: %# ; Name: %# ; Book: %#", task.id, task.name, task.book.name);
}
}
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error
{
NSLog(#"Encountered an error: %#", error);
}
#end
Step1. Since your TableView is an IBOutlet, check you tableView datasource and delegate mappings in the .xib file for this view controller. My doubt is that the hooking is not correct.
Step2. If the hook ups in .xib file are correct, then you should do
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects
{
self.searchResults = objects;
NSLog(#"%#", searchResults)
[self.table reloadData];
....
}
and check if the NSLog is logging the searchResults array. If that's empty for some reason, your numberOfRowsInSection delegate method will return 0 and hence your tableView will be empty.
Hope this helps.
in the first line
#interface FirstViewController : UIViewController <UITableViewDataSource, RKObjectLoaderDelegate>
{
UISearchDisplayController *searchDisplayController;
UISearchDisplayController *searchBar;
UITableView *table;
NSArray *allItems;
NSArray *searchResults;
}
replace this line with below code
#interface FirstViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, RKObjectLoaderDelegate>
{
UISearchDisplayController *searchDisplayController;
UISearchDisplayController *searchBar;
IBOutlet UITableView *table;
NSArray *allItems;
NSArray *searchResults;
}
and connect the tableview delegate and tableview datasource in interface builder.
Hope it helps.
I have two view controllers. The CardWallet View Controller is my table view. Then the AddCard View Controller is where I input values for a new instance of an object named Card. So far, I am adding those Card instances in an array named myWallet which is in my CardWallet View Controller using a delegate and it works.
What I want is, after clicking the button in my AddCard View Controller, a new table cell will appear in my Card Wallet View, with the name depending on the recently added instance of Card. Below is my code, kindly check why is it that when I'm finished adding a new instance of Card, nothing appears in my table. I've done some research and went through some tutorials, this one is good, http://kurrytran.blogspot.com/2011/10/ios-5-storyboard-and.html, it helped me a lot regarding table view controllers. However, the tutorial doesn't cater my main concern for it's table's values only come from an array with static values.
Thanks!
CardWalletViewController.h
#import <UIKit/UIKit.h>
#interface CardWalletViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
}
#property (nonatomic, strong) NSMutableArray *myWallet;
-(void) printArrayContents;
#end
CardWalletViewController.m
#import "CardWalletViewController.h"
#import "AddCardViewController.h"
#import "Card.h"
#interface CardWalletViewController () <AddCardDelegate>
#end
#implementation CardWalletViewController
#synthesize myWallet = _myWallet;
- (NSMutableArray *) myWallet
{
if (_myWallet == nil) _myWallet = [[NSMutableArray alloc] init];
return _myWallet;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showAddCardVC"]) {
AddCardViewController *addCardVC = (AddCardViewController *)segue.destinationViewController;
addCardVC.delegate = self;
}
}
- (void)printArrayContents
{
// I want to show the name of each instance of card
for ( int i = 0; i < self.myWallet.count; i++) {
Card *cardDummy = [self.myWallet objectAtIndex:i];
NSLog(#"Element %i is %#", i,cardDummy.name );
}
}
- (void)addCardViewController:(AddCardViewController *)sender didCreateCard:(Card *)newCard
{
// insert a new card to the array
[self.myWallet addObject:newCard];
[self printArrayContents];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
}
- (void)viewWillAppear:(BOOL)animated
{
}
- (void)viewWillDisappear:(BOOL)animated
{
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//this method will return the number of rows to be shown
return self.myWallet.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
//---------- CELL BACKGROUND IMAGE -----------------------------
UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.frame];
UIImage *image = [UIImage imageNamed:#"LightGrey.png"];
imageView.image = image;
cell.backgroundView = imageView;
[[cell textLabel] setBackgroundColor:[UIColor clearColor]];
[[cell detailTextLabel] setBackgroundColor:[UIColor clearColor]];
//this will show the name of the card instances stored in the array
//
for ( int i = 0; i < self.myWallet.count; i++) {
Card *cardDummy = [self.myWallet objectAtIndex:i];
cell.textLabel.text = cardDummy.name;
}
//Arrow
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
AddCardViewController.h
#import <UIKit/UIKit.h>
#import "Card.h"
#class AddCardViewController;
#protocol AddCardDelegate <NSObject>
- (void)addCardViewController:(AddCardViewController *)sender
didCreateCard:(Card *) newCard;
#end
#interface AddCardViewController : UIViewController <UITextFieldDelegate>
#property (strong, nonatomic) IBOutlet UITextField *cardNameTextField;
#property (strong, nonatomic) IBOutlet UITextField *pinTextField;
#property (strong, nonatomic) IBOutlet UITextField *pointsTextField;
#property (nonatomic, strong) id <AddCardDelegate> delegate;
#end
AddCardViewController.m
#import "AddCardViewController.h"
#import "Card.h"
#import "CardWalletViewController.h"
#interface AddCardViewController ()
#end
#implementation AddCardViewController
#synthesize cardNameTextField = _cardNameTextField;
#synthesize pinTextField = _pinTextField;
#synthesize pointsTextField = _pointsTextField;
#synthesize delegate = _delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.cardNameTextField becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated
{
}
- (BOOL) textFieldShouldReturn:(UITextField *)textField{
if ([textField.text length]) {
[self.cardNameTextField resignFirstResponder];
[self.pinTextField resignFirstResponder];
[self.pointsTextField resignFirstResponder];
return YES;
}
else {
return NO;
}
}
- (void)viewDidLoad
{
self.cardNameTextField.delegate = self;
self.pinTextField.delegate = self;
self.pointsTextField.delegate = self;
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setCardNameTextField:nil];
[self setPinTextField:nil];
[self setPointsTextField:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)addCard:(id)sender
{
Card *myNewCard = [[Card alloc] init];
myNewCard.name = self.cardNameTextField.text;
myNewCard.pin = self.pinTextField.text;
myNewCard.points = [self.pointsTextField.text intValue];
// to check if the text fields were filled up by the user
if ([self.cardNameTextField.text length] && [self.pinTextField.text length] && [self.pointsTextField.text length])
{
[[self presentingViewController] dismissModalViewControllerAnimated:YES];
NSLog(#"name saved %#", myNewCard.name);
NSLog(#"pin saved %#", myNewCard.pin);
NSLog(#"points saved %i", myNewCard.points);
[self.delegate addCardViewController:self didCreateCard:myNewCard];
// to check if there is a delegate
if (self.delegate){
NSLog(#"delegate is not nil");
}
}
}
#end
Card.h
#import <Foundation/Foundation.h>
#interface Card : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *pin;
#property (nonatomic) int points;
#end
Card.m
#import "Card.h"
#implementation Card
#synthesize name = _name;
#synthesize pin = _pin;
#synthesize points = _points;
#end
I should get the obvious question out of the way before anyone starts dwelling too deep into this - do you have some mechanism of reloading the data after you add a new card (e.g. call [tableView reloadData] from the CardWalletViewController)? I didn't see anything like that, and I've always used this whenever I add something new to a table.*
*If the table contains too much data, you may want to reload only a part of it.
Update 1: Class Inheritance
Every Objective C class has to inherit from some other class in the hierarchy. By default, unless you say otherwise, all of your custom classes will inherit from NSObject, which is the most generic object out there (equivalent of Object, if you've done Java programming). Changing the parent class is done by simply changing the class after the : in your interface declaration. So when you say
#interface CardWalletViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
what you are saying is "declare a CardWallerViewController custom class that inherits from UIViewController and implements the UITableViewDelegate and UITableViewDataSource protocols" (if you don't know what protocols are, ask).
Now, back to your question. Changing the parent class should be easy now - you just change that : UIViewController to : UITableViewController and you are done. After you do this, your CardWallerViewController (also, "Waller", really?) will behave like a UITableView, not like a generic UIView. When doing this, you will also not need to tell it to implement the delegate and dataSource protocols - UITableViewController does that by default.
As a final note, when you add new files to your Xcode project, you can tell the program which class you want to inherit from. It defaults to UIView for views, but that's simply because this is the most generic view class. As you begin to use more specific classes (UITableViewController, UIPickerViewController, UITableViewCell, to name a few), changing the parent class off the bat will prove to be more than helpful.
Update 2: UITableViewCells
That for-loop you've got going there is a (relatively) lot of work you don't need to do. Since your table corresponds directly to your myWallet property, this means that the cell in row N of your table will represent the card at index N of your array. You can use that to your advantage. In the tableView:cellForRowAtIndexPath: method, you tell the program what to do with the cell at the specific indexPath (which is really just section + row for that table). The trick is, this method instantiates the cells one at a time. So instead of doing the for-loop, you can just say (at the end)
cell.textLabel.text = [self.myWallet objectAtIndex:indextPath.row].name;
For any cell in row N, this will look at the Nth Card object inside myWallet and use its name to set the cell's textLabel.text. If it gives you problems, save [self.myWallet objectAtIndex:indextPath.row] in some tempCard object, and then do cell.textLabel.text = tempCard.name. This is also the proper way to populate cells in a tableView - you only care about one cell at a time, because that's how the method works anyway. Imagine if you had 1,000,000 Cards inside your array - doing the for-loop would force the program to go through the array 1,000,000 times for each cell. Say hello to a 1,000,000,000,000 operations :)
i think u can add the imageview as subview to cell
UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.frame];
UIImage *image = [UIImage imageNamed:#"LightGrey.png"];
imageView.image = image;
[cell addSubview:imageView];
[[cell textLabel] setBackgroundColor:[UIColor clearColor]];
[[cell detailTextLabel] setBackgroundColor:[UIColor clearColor]];
I have made a sample project that reproduces this issue which contains two views:
root header:
#import <UIKit/UIKit.h>
#import "view2.h"
#interface RootViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>{
view2 *secondView;
UITableView *table;
NSArray *array;
}
#property (nonatomic, retain) view2 *secondView;
#property (nonatomic, retain) IBOutlet UITableView *table;
#property (nonatomic, retain) NSArray *array;
#end
root main:
#import "RootViewController.h"
#implementation RootViewController
#synthesize table, array, secondView;
- (void)viewDidLoad
{
[super viewDidLoad];
if(self.array == nil){
self.array = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", nil];
}
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload
{
[super viewDidUnload];
table = nil;
array = nil;
secondView = nil;
}
- (void)dealloc
{
[table release];
[array release];
[secondView release];
[super dealloc];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [array 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];
}
cell.textLabel.text = [array objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (secondView == nil) {
secondView = [[view2 alloc] init];
}
[self.navigationController pushViewController:secondView animated:YES];
}
#end
view2 simple contains a label with the text "view 2" for identification purposes.
All this code is doing in the root controller is creating an array with the values 1,2,3,4 and binding this text as rows to the table, clicking any row pushes view 2 onto the stack.
if you load up the app in the simulator using the leaks instruments tool, click on any row so that view2 is displayed and then simulate an error warning the following leaks appear:
image
for the line:
self.array = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", nil];
this is causing me a lot of problems in my main app as i am using arrays to provide data in tables all over the place.
i have tried various ways to fix this such as declaring the array in different ways to no avail.
any help is greatly appreciated!
thanks
In viewDidUnload you're mixing up property vs. direct ivar access.
array = nil simply sets the ivar to nil without using the synthesized accessor method. You have to use the dot notation: self.array = nil;
This way the accessor setArray: is used which handles memory management for you.
Mixing up ivars and properties is a frequent problem amongst Objective-C beginners. The confusion can easily be prevented by always using different names for properties and ivars:
#synthesize array = _array;
You can just leave out the ivar declaration in the class's #interface or name it as in the #synthesize directive.
I have a RootViewController with 2 tableViews as subviews (created in IB) each with their own tableViewController class (handleing fetchRequests etc.)
1 tableView is static (no data changed by user or modelViews).
tableView 2 has a button in the header which presents an imagePickerController.
No issues so far.
Problem is, when i dismiss the imagePicker
[self dismissModalViewControllerAnimated:YES];
TableView 2 becomes full screen i have tried
[[self rootViewController] dismissModalViewControllerAnimated:YES]
Nothing happens at all. It sticks on the image picker.
I suspect this is due to there being very little of the view being created programmaticaly.
Any ideas?
Thanks in advance.
DetartrateD
-(IBAction)addImageTableAPressed {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
[self presentModalViewController:imagePicker animated:YES];
[imagePicker release];
}
RootViewController
|| ||
|| ||
\/ \/ addImageTableAPressed
TableViewControlA TableViewControlB --------------------->modelViewController
To resolve mananagedObjectContect.....
- (void)viewDidLoad {...
if(managedObjectContext == nil)
{
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}
...
}
As I mentioned in one of my comments, I would prefer having a single view controller managing the two table views. Define a UIView (the rootView) including 2 subviews (tableViewA and tableViewB). Your RootViewController's view will be rootView, and this controller will have to be the data source and delegate of both table views. The code I will give here is by no means complete nor optimal, but gives you a good idea of what is needed to implement my solution.
For example:
#interface RootViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *dataArrayA;
NSArray *dataArrayB;
UITableView tableViewA;
UITableView tableViewB;
NSManagedObjectContext *context;
}
#property (nonatomic, retain) NSArray *dataArrayA;
#property (nonatomic, retain) NSArray *dataArrayB;
// in IB, link the dataSource and delegate outlets of both tables to RootViewController
#property (nonatomic, retain) IBOutlet UITableView tableViewA;
#property (nonatomic, retain) IBOutlet UITableView tableViewB;
// this property will allow you to pass the MOC to the RootViewController from
// the parent view controller, instead of accessing the app delegate from RootViewController
#property (nonatomic, retain) NSManagedObjectContext *context;
// ... etc.
#end
#implementation RootViewController
#synthesize dataArrayA;
#synthesize dataArrayB;
#synthesize tableViewA;
#synthesize tableViewB;
#synthesize context;
// initialize dataArrayA and dataArrayB
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error = nil;
// initialize and configure your fetch request for data going into tableViewA
NSFetchRequest fetchRequestA = [[NSFetchRequest alloc] init];
// configure the entity, sort descriptors, predicate, etc.
// ...
// perform the fetch
self.dataArrayA = [context executeFetchRequest:fetchRequestA error:&error];
// do the same for the data going into tableViewB - the code is very similar, you
// could factor it out in a private method instead of duplicating it here
// NSFetchRequest fetchRequestB = [[NSFetchRequest alloc] init];
// omitting the details ... etc.
self.dataArrayB = [context executeFetchRequest:fetchRequestB error:&error];
// release objects you don't need anymore, according to memory management rules
[fetchRequestA release];
[fetchRequestB release];
}
// Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// if you have a different number of sections in tableViewA and tableViewB
/*
if (tableView == tableViewA) {
return ??;
} else {
return ??
}
*/
// otherwise, if both table views contain one section
return 1;
}
// Customize the number of rows in each table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == tableViewA) {
return [dataArrayA count];
} else {
return [dataArrayB count];
}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (tableView == tableViewA) {
// get the data for the current row in tableViewA
id objectA = [dataArrayA objectAtIndex:indexPath.row];
// configure the cell for tableViewA
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierA];
// etc...
} else {
// get the data for the current row in tableViewB
id objectB = [dataArrayB objectAtIndex:indexPath.row];
// configure the cell for tableViewB
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierB];
// etc...
}
return cell;
}
// And so on, the same idea applies for the other UITableViewDelegate you would need to
// implement...
- (void)dealloc {
[dataArrayA release];
[dataArrayB release];
[tableViewA release];
[tableViewB release];
[context release];
// etc...
[super dealloc];
}
#end
I hope you'll find this useful.