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.
Related
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 am a total newbie on iOS development. After numerous tries; with lots of sample codes I managed to parse a json string from my server and able to display the results in a dynamic tableview. My problem is I cannot make the cells clickable so they would pass the id and the label to another view where another json parse will be performed to display the details of the row.
Below is my code:
#import "jsonviewcontroller.h"
#import "CJSONDeserializer.h"
#import "Otel_ItemViewController.h"
#implementation jsonviewcontroller
#synthesize tableview;
#synthesize rows;
- (void)dealloc {
[rows release];
[tableview release];
[super dealloc];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [rows count];
}
// 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.
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
cell.textLabel.text = [dict objectForKey:#"C_NAME"];
cell.detailTextLabel.text = [dict objectForKey:#"CAT_ID"];
return cell;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"http://zskript.net/categories.php"];
NSString *jsonreturn = [[NSString alloc] initWithContentsOfURL:url];
NSLog(jsonreturn); // Look at the console and you can see what the restults are
NSData *jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSError *error = nil;
// In "real" code you should surround this with try and catch
NSDictionary * dict = [[[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error] retain];
if (dict)
{
rows = [dict objectForKey:#"users"];
}
NSLog(#"Array: %#",rows);
[jsonreturn release];
}
// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"ShowSelectedMovie"]) {
// Get reference to the destination view controller
Otel_ItemViewController *vc = [segue destinationViewController];
// get the selected index
NSInteger selectedIndex = [[self.tableview indexPathForSelectedRow] row];
// Pass the name and index of our film
[vc setSelectedItem:[NSString stringWithFormat:#"%#", [rows objectAtIndex:selectedIndex]]];
[vc setSelectedIndex:selectedIndex];
}
}
- (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;
}
#end
And below is the view that will display the detail:
#import "Otel_ItemViewController.h"
#implementation Otel_ItemViewController
#synthesize selectedIndex, selectedItem;
- (void)viewDidLoad
{
[super viewDidLoad];
[outputLabel setText:selectedItem];
[outputText setText:selectedItem];
[outputImage setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d.jpg", selectedIndex]]];
}
#end
Currently, when I click the cells in the table, Although I have set it to push to the next view, nothing happens. Would someone please advise?
Here is the updated code.
My root view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
DetailViewController *controller = [[DetailViewController alloc] init];
controller.CATNAME = [dict objectForKey:#"C_NAME"];
controller.CATNUMBER = [dict objectForKey:#"CAT_ID"];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
And here is DetailViewController.h:
#interface DetailViewController : UIViewController {
NSString *CATNAME;
NSInteger CATNUMBER;
IBOutlet UILabel *labelId;
IBOutlet UILabel *LabelName;
}
#property (nonatomic) NSInteger CATNUMBER;
#property (nonatomic, retain) NSString *CATNAME;
#end
And DetailViewController.m:
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize CATNAME, CATNUMBER;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void) viewDidLoad
{
LabelName.Text = CATNAME;
labelId = CATNUMBER;
// [LabelName setText:CATNAME];
// [labelId setText:CATNUMBER];
}
You have to implement this method
EDIT: Loading a new view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
MyNewViewController *controller = [[MyNewViewController alloc] init];
controller.C_NAME = [dict objectForKey:#"C_NAME"];
controller.CAT_ID = [dict objectForKey:#"CAT_ID"];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
In the code above I assume you are using a navigation controller (which is the easiest way to do what you want to be doing). Also I am assuming you have a class that inherits from UIViewController that you want to have displayed. I am also assuming that this class which I called MyNewViewController in my example has two members and properties called C_NAME and CAT_ID respectively.
- (void) viewDidLoad
{
labelName.Text = C_NAME;
labelId = CAT_ID
}
The above my be incorrect as I am doing it out of memory. But the principal stays the same, if you passed the variables correctly it should work, you can have a look at my blog it still needs some work done on it, but it has a nice beginners post and shows how to edit the text of the label. In the code above I am assuming your view contains two labels labelName and labelId respectfully.
In there you have access to what cell was selected and you can then define what needs to happen.
I'm fairly new to iOS development, but have yet to find a fix for this after extensive searching.
The issue is that when a table cell is clicked, it's title information (built from an array at index) is sent to a UILabel on the Detail view controller. Then after clicking back to the MasterViewController TableViewCells, and selecting a different cell, it's title is not passed to the UILabel, and the previously or first item clicked information is retained.
This may be a release/retain issue, or a table reloadData issue but no matter where I try to add these in for my objects or table it won't reset the "currentNodeTitle" on DetailViewController to be the newly clicked cell before sending it to the UILabel.
Here is my setup:
I have a table with cells created from an NSMutableArray called nodeTitles.
The NSLog output of the array data is
nodeTitles: (
Issue,
"Issue 2",
"Issue 3",
"Issue 4",
"Issue 5",
"Issue 6",
"Issue 7"
)
In MasterViewController.h:
#class DetailViewController;
#interface MasterViewController : UITableViewController {
NSMutableArray * nodeTitles;
}
#property (strong, nonatomic) DetailViewController *detailViewController;
#property (nonatomic, retain) NSMutableArray *nodeTitles;
#end
In MasterViewController.m:
From the array each cell is created with the contents of each indexed row.
nodeTitles = [[NSMutableArray alloc]initWithObjects:#"Issue", #"Issue 2",#"Issue 3", #"Issue 4", #"Issue 5", #"Issue 6", #"Issue 7", nil];
cellForRowAtIndexPath:
- (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];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
//*only thing added*
cell.textLabel.text = [self.nodeTitles objectAtIndex: [indexPath row]];
//*end only thing added*
return cell;}
Then for the didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
if (!self.detailViewController) {
self.detailViewController = [[[DetailViewController alloc] initWithNibName:#"DetailViewController_iPhone" bundle:nil] autorelease];
self.detailViewController.currentNodeTitle = [nodeTitles objectAtIndex:indexPath.row];
//[contentTitleArray release];
}
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
}
Everything is working at this point. I've got a list of table view cells, with the content from my array and when clicked, I've linked these to a UILabel.
My Dealloc, viewDidUnload, viewWillAppear, viewDidAppear and viewWillDissappear in MasterViewController:
- (void)dealloc
{
[self.nodeTitles release];
[_detailViewController release];
[super dealloc];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
self.nodeTitles = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.tableView reloadData];
}
My DetailViewController.h:
#interface DetailViewController : UIViewController <UISplitViewControllerDelegate> {
IBOutlet UILabel *detailDescriptionLabel;
NSMutableString *currentNodeTitle;
}
#property (strong, nonatomic) id detailItem;
#property (nonatomic, retain) IBOutlet UILabel *detailDescriptionLabel;
#property (nonatomic, retain)NSMutableString *currentNodeTitle;
#end
DetailViewController.m:
#interface DetailViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
#end
#implementation DetailViewController
#synthesize detailItem = _detailItem;
#synthesize detailDescriptionLabel = _detailDescriptionLabel;
#synthesize masterPopoverController = _masterPopoverController;
#synthesize currentNodeTitle;
- (void)dealloc
{
currentNodeTitle = nil;
[_detailItem release];
[_detailDescriptionLabel release];
[_masterPopoverController release];
[super dealloc];
}
In the viewDidLoad on DetailViewController.m, I'm setting the UILabel item with:
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = currentNodeTitle;
_detailDescriptionLabel.text = currentNodeTitle;
[self configureView];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[currentNodeTitle release];
}
After clicking on an item, and the clicking back to the master controller, how can I get a newly selected cell title to show in the UILabel on the DetailViewController?
thanks~
if (!self.detailViewController) {
self.detailViewController.currentNodeTitle = [nodeTitles objectAtIndex:indexPath.row];
}
I think this IF statement is not executed again since detailViewController is valid.
Move the currentNodeTitle assignment out of the IF block.
EDIT
You are retaining the detailViewController, so it looks like viewDidLoad is being called only once and that is the only place where you use the currentNodeTitle.
Try moving the
self.title = currentNodeTitle;
_detailDescriptionLabel.text = currentNodeTitle;
to viewWillAppear: since it will be called every time you push the detailViewController.
When you are making your new label then just reload the table. One think also you should follow that, make a label in the if(cell == nil) { [cell.contentView addSubview:label]; } then make tag of this label then after this condition review the label label = (UILabel*)[cell.contentView viewWithTag:labeltag]
may be then it will help your problem.
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 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.