UITableView corruption upon scroll - iphone

Im trying to get a UITableView to display items contained within an array.
This array is contained within a class (HX_ParkingSearch).
I have a ref to this class that contains the array inside the app delegate class, to enable the views to access it.
Problem is that I get one page of results displaying correctly inside the tableview
but when i try and scroll down an exception occurs when trying to access the next item in the array.
It turns out that when i scroll down and the cellForRowAtIndexPath method fires,
the items inside the array are invalid and appear to have been released but i dont understand where
they are being released!
Does anyone have any ideas because this is really doing my head in now!
Many thanks,
Chris.
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
HX_ParkingLocation *location;
bookingApp2AppDelegate *del = (bookingApp2AppDelegate *) [[UIApplication sharedApplication] delegate];
NSMutableArray* array = [del.parkingSearch locations];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
location = (HX_ParkingLocation*) [array objectAtIndex: [indexPath row] ];
return cell;
}
#import <Foundation/Foundation.h>
#interface HX_ParkingLocation : NSObject
{
NSString *name;
}
#property(retain,nonatomic) NSString* name;
/*
Initialises this Location instance by passing in the name and code of the location and the URL of the webapi product endpoint.
The URL is used to find available products at this location.
*/
-(id) initWithName: (NSString*) n;
#end
#import <Foundation/Foundation.h>
#interface HX_ParkingSearch : NSObject
{
NSMutableArray* locations;
}
#property (retain) NSMutableArray* locations;
-(BOOL) loadLocations;
#end
#import "HX_ParkingSearch.h"
#import "HX_Parking_Location.h"
#implementation HX_ParkingSearch
#synthesize locations;
//Finds the locations
-(BOOL) loadLocations
{
[locations release];
//Create array to hold locations
locations = [[NSMutableArray alloc] initWithCapacity:30];
//Loop through all returned locations
for(int i=0;i<15;i++)
{
//Get location name
NSString* n = [NSString stringWithFormat:#"Item #%i",i ];
//Create location instance, which retrieves availability and product information and stores the information in location object.
HX_ParkingLocation* location = [[HX_ParkingLocation alloc] initWithName:n];
//add to array
[locations addObject:location];
}
return YES;
}
#end
#import <UIKit/UIKit.h>
#import "HX_ParkingSearch.h"
#interface bookingApp2AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
HX_ParkingSearch *parkingSearch;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#property (retain) HX_ParkingSearch *parkingSearch;
#end
#implementation bookingApp2AppDelegate
#synthesize window;
#synthesize navigationController;
#synthesize parkingSearch;
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
//Create new parking search instance by specifying the endpoint urls
HX_ParkingSearch* search = [[HX_ParkingSearch alloc] init];
[search loadLocations];
parkingSearch = search;
//NSLog(#"Search Retain count = %i" ,[search retainCount]);
[window addSubview:[navigationController view]];
//[window addSubview:[navigationController initWithNibName:#"VC_Locations" bundle:[NSBundle mainBundle]]];
[window makeKeyAndVisible];
}

Is there a reason that you're init-ing your array with a capacity of 30 in loadLocations, but only inserting 15 items?

what's in this method for your datasource?:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
and also:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
the tableview may just be trying to grab an index that's out of bounds of locations

Related

Passing Data from Dynamic tableView to Static TableView

Hi Let me try to clarify my issue. I have two TableViews, one is static and the other is dynamic. The static= RootVC and Dynamic=FirstVC. In FirstVC i have data that I want to select,save and pass the saved data to a UILabel in RootVC. 1)When I run my App data is selected however it is not saved or passed to my rooVC. I was using delegates and was advice not to use "delegate" but use "Blocks". But still i'm facing the same issue. Here is my code:
in rootVC.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController
{
NSString *getRepeatLabel;
}
#property (strong, nonatomic) IBOutlet UILabel *repeatLabel;
#property (strong, nonatomic) IBOutlet UILabel *repeatDetail;
#property (nonatomic,strong) NSString *getRepeatLabel;
#end
in my rootVC.m
#import "RootViewController.h"
#interface RootViewController ()
#end
#implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_repeatLabel.text = #"Repeat";
_repeatDetail.text = getRepeatLabel;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationController = segue.destinationViewController;
if( [destinationController isKindOfClass:[FirstViewController class]] )
{
[(FirstViewController *)destinationController setCompletionBlock:^(NSString *getRepeatLabel;)
{
// do something here with your string // maybe you must reload your table // it depends on where your returning data needs to display <--------Not sure what to do here
// NSDateFormatter*dateFormatter = [[NSDateFormatter alloc]init];
// NSArray*days = [dateFormatter shortWeekdaySymbols]; <------Here I would like when data is selected to show days in short symbol
NSLog (#"The selected day/s is %#", getRepeatLabel); <---nothing displaying on console
}];
}
}
#end
in FirstVC.h
#import <UIKit/UIKit.h>
#import "RootViewController.h"
typedef void(^WeekdayCompletionBlock)(NSString *dayName);
#interface FirstViewController : UITableViewController
{
NSString *dayName;
}
#property (nonatomic, strong) WeekdayCompletionBlock completionBlock;
#property (nonatomic,strong) NSString *dayName;
- (IBAction)save:(id)sender;
#end
in FirstVC.m
#import "FirstViewController.h"
#import "RootViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize completionBlock;
#synthesize dayName;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Initialize table data
completionBlock = [NSArray arrayWithObjects:#"Sunday", #"Monday", #"Tuesday", #"Wednesday", #"Thursday", #"Friday", #"Saturday", nil];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [completionBlock count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"RepeatCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [completionBlock objectAtIndex:indexPath.row];
return cell;
}
// Called after the user changes the selection.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog (#"The selected day/s is %#", [completionBlock objectAtIndex:indexPath.row]);
_getRepeatLabel = completionBlock; //<-----------string from RootVC gives error "undeclared _getRepeatLabel"
}
- (IBAction)save:(id)sender
{
NSUserDefaults *myNewWeekString = [NSUserDefaults standardUserDefaults];
[myNewWeekString setObject:completionBlock forKey:#"%#"];
[myNewWeekString synchronize];
self.completionBlock(myNewDayOfWeekString) <------error myNewDayOfWeekString undeclared and if i declare it here it complains about incompatibility
}
#end
Your code is a little bit wrong. I think you don't really understand block.
You want to pass more than string so the best way to do that is via array. Change block definition to accept array instead of string:
typedef void(^WeekdayCompletionBlock)(NSArray *dayName);
Change declaration of your property in FirstVC.h to:
#property (nonatomic, copy) NSArray *completionBlock; //This is your array, you use it as data source, It's not a block
//Add your block property
// This is your block property you will use it to pass the data between view controllers
#property (copy) WeekdayCompletionBlock returnBlock;
//Add property to keep your selected days
#property (nonatomic, strong) NSMutableArray *returnArray;
Add this line to viewDidLoad method:
self.returnArray = [[NSMutableArray alloc] init];
Change your didSelectRowAtIndexPath method in FirstVC.m to:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
//remove data from array
[self.returnArray removeObject:[completionBlock objectAtIndex:indexPath.row]];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//add data to array
[self.returnArray addObject:[completionBlock objectAtIndex:indexPath.row]];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
In your save: method call block and pass value to rootVC replace line:
self.completionBlock(myNewDayOfWeekString);
with:
if (self.returnBlock)
{
self.returnBlock(self.returnArray);
}
[self.navigationController popViewControllerAnimated:YES];
The last change left to do is set up ablok in your rootVC.m file. Replace line:
[(FirstViewController *)destinationController setCompletionBlock:
with (nsstring needs to be replaced with nsarray - you pass array with all of the selected data)
[(FirstViewController *)destinationController setCompletionBlock:^(NSArray *getRepeatLabel)
You set up block not NSArray.
I don't know what are you trying to do here:
[myNewWeekString setObject:completionBlock forKey:#"%#"];
You are using %# as a key. It should be text for example #"MY_KEY_FOR_ACCESING_DAYSOFWEEK". You are saving completionBlock it's all of your days if you want to save just selected days replace it with self.returnArray.
Hope this help.

how to send data backword from second class to first class in iphone

i am try to exchange the one cell to the value of other cell but my value is not transfer proper bec my value is passing second class to first class but not string value is passing array reference why what is the mistake in my code can u help me
for this i create delegate method
this is my originstart.h file
import
#import "RegexKitLiteDemoAppDelegate.h"
#class airport;
#protocol OriginstartDelegate <NSObject>
#required
- (void)Originstart:(NSString *)Origin forIndexPath:(NSIndexPath *)indexPath;
#end
#interface Originstart : UITableViewController {
RegexKitLiteDemoAppDelegate *app;
airport *selectedAirport;
}
#property(nonatomic,retain)NSString*name;
#property(nonatomic,retain)airport *selectedAirport;
#property (nonatomic, copy) NSIndexPath *indexPathToChange;
#property (nonatomic, assign) id<OriginstartDelegate> delegate;
#end
this is my orginestart.m file
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return app.lstAirports.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:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
airport* a=(airport*)[app.lstAirports objectAtIndex:indexPath.row];
NSLog(#"str:%#",a);
cell.textLabel.text =a.Name;
cell.detailTextLabel.text=a.Code;
// Configure the cell...
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *newStation = [[app.lstAirports objectAtIndex:indexPath.row]description];
[delegate Originstart:newStation forIndexPath:self.indexPathToChange];
[self.navigationController popViewControllerAnimated:YES];
}
this is my Tfirst.m file
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"accessory selected");
if ([indexPath section] == 0)
{
// load the appropriate view for the accessory selected
if ([indexPath row] == 0)
{
Originstart *viewtwo = [[Originstart alloc] initWithStyle:UITableViewStyleGrouped];
viewtwo.indexPathToChange = indexPath;
viewtwo.delegate = self;
[self.navigationController pushViewController:viewtwo animated:YES];
[viewtwo release];
}
else{
NSLog(#"accessory right");
}
}
}
#pragma mark - Delegate protocol method
- (void)Originstart:(NSString *)Origin forIndexPath:(NSIndexPath *)indexPath {
[datasource replaceObjectAtIndex:indexPath.row withObject:Origin];
[self.tableView reloadData];
}
this is my airport.h file where i create two string variable and one array for take value and display for in originstart classwith name and code i writen this in here in class
originstart.m airport* a=(airport*)[app.lstAirports objectAtIndex:indexPath.row];
NSLog(#"str:%#",a);
cell.textLabel.text =a.Name;
cell.detailTextLabel.text=a.Code;
and final this class arport class
//
// airport.h
// RegexKitLiteDemo
//
// Created by pradeep.yadav on 9/13/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface airport : NSObject {
NSString *Code;
NSString *Name;
NSArray *DestinationAirports;
}
#property(nonatomic,retain)NSString *Code;
#property(nonatomic,retain)NSString *Name;
#property(nonatomic,retain)NSArray *DestinationAirports;
#end
To achieve what u want , just try these steps:
1.First, create a property of first class in the second class.
2.In the first class create those variables or objects as properties to which u want to pass the data from the second class.
3.(Hope u are using navigation controller based app ) While pushing from the first to second view controller u create a instance of the second class and pass it pushViewController method. Add the following line of code along with it.....
secondViewObj.firstView=self;
where secondViewObj is the instance of the secondViewController class and firstView is the property declared in that second class.
4.Now in the second class when u want to pass the data to first class just add the following line of code:
firstView.dataMember1=value;
where dataMember1 is the variable or object which was created as a property as mentioned in the 2nd point. Hope this helps.
In the FirstViewController class where u push to SecondviewController have the following piece of code:
SecondViewController *svc = [SecondViewController alloc]init];
svc.vc = self;
[self.navigationController pushViewController:svc animated:YES];
and also have a property declared for the data u want to pass to the firstView Controller.
In this case it s going to be a integer variable.
#property (nonatomic) int selectedValue and also synthesize it.
Now in the secondViewController , declare a property for the firstViewController like this
#property (nonatomic, retain) FirstViewController *vc; and synthesize it.
Where u want to pass the data to the FirstViewController have the following code,
vc.selectedValue = 1;
Now after popping back to the firstViewController , if u chk the value of the selectedValue variable u will find the value has been passed from second to first.
Hope this helps..
The simplest apprroach is to set an object in FirstViewController here is the way
SecondViewController
FirstViewController *instanceOfFirstView;
#property (nonatomic, retain) FirstViewController *instanceOfFirstView;
#synthesize instanceOfFirstView;
[instanceOfFirstView setValueNeedTobeBroughtFromSecondView:#"VAlue set"]
FirstViewController
NSString *valueNeedTobeBroughtFromSecondView;
#property (nonatomic, retain) NSString *valueNeedTobeBroughtFromSecondView;
#synthesize valueNeedTobeBroughtFromSecondView;
SecondViewController *aSecondViewController=[[SecondViewController alloc] init];
[aSecondViewController setInstanceOfFirstView: self];

How to sum contents of reusable Custom Cells for a Table View that acts as a Calculator

I built a table view, with Custom Table View Cells, where each cell has several UITextFields that allow the user to enter numbers that provide a calculation. The cell hast a subtotal UITextField that provides the result for that cell.
My problem is that I want to take the subtotal of that cell, and of each additional cell that the User uses, and provide a Total on a different UIView.
I do not know how to take the value of each subtotal for each cell, and furthermore, how can I know how many cells the user used, and all of this in order to provide the user with a TOTAL in a different view.
What would be the best way to achieve this?
Here is the Code.
I want to add new cells and be able to add them to the calculation. But each cell will hold different values that the user will provide.
Best!
#import <UIKit/UIKit.h>
#interface ProCalculator : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITextField *totalField;
UITextField *differenceField;
UITextField *productField;
UITextField *price1Field;
UITextField *discount1P1Field;
UITextField *discount2P1Field;
UITextField *subtotal1Field;
UITextField *product2Field;
UITextField *price2Field;
UITextField *discount1P2Field;
UITextField *discount2P2Field;
UITextField *subtotal2Field;
}
#property (nonatomic, retain) IBOutlet UITextField *totalField;
#property (nonatomic, retain) IBOutlet UITextField *differenceField;
#property (nonatomic, retain) IBOutlet UITextField *productField;
#property (nonatomic, retain) IBOutlet UITextField *price1Field;
#property (nonatomic, retain) IBOutlet UITextField *discount1P1Field;
#property (nonatomic, retain) IBOutlet UITextField *discount2P1Field;
#property (nonatomic, retain) IBOutlet UITextField *subtotal1Field;
#property (nonatomic, retain) IBOutlet UITextField *product2Field;
#property (nonatomic, retain) IBOutlet UITextField *price2Field;
#property (nonatomic, retain) IBOutlet UITextField *discount1P2Field;
#property (nonatomic, retain) IBOutlet UITextField *discount2P2Field;
#property (nonatomic, retain) IBOutlet UITextField *subtotal2Field;
-(IBAction)calculateDiscounts:(id)sender;
-(IBAction)removeKeyboard;
#end
#import "ProCalculator.h"
#import "ProductCell.h"
#implementation ProCalculator
#synthesize totalField, differenceField, productField, price1Field, discount1P1Field, discount2P1Field, subtotal1Field;
#synthesize product2Field, price2Field, discount1P2Field, discount2P2Field, subtotal2Field;
-(IBAction)calculateDiscounts:(id)sender
{
//Calculate product 1 within first Cell
NSString *firstPrice = self.price1Field.text;
NSString *discount1P1 = self.discount1P1Field.text;
NSString *discount2P1 = self.discount2P1Field.text;
double subtotal1WithDiscount1;
double subtotal1WithDiscount2;
double firstPriceDouble = [firstPrice doubleValue];
double discount1P1Double = [discount1P1 doubleValue];
double discount2P1Double = [discount2P1 doubleValue];
double percentageDiscount1P1;
double percentageDiscount2P1;
double savings1P1;
double savings2P1;
percentageDiscount1P1 = discount1P1Double / 100;
percentageDiscount2P1 = discount2P1Double / 100;
savings1P1 = firstPriceDouble * percentageDiscount1P1;
subtotal1WithDiscount1 = firstPriceDouble - savings1P1;
savings2P1 = subtotal1WithDiscount1 * percentageDiscount2P1;
subtotal1WithDiscount2 = subtotal1WithDiscount1 - savings2P1;
NSString *finalSubtotal1 = [[NSString alloc] initWithFormat:#"$ %.02f", subtotal1WithDiscount2];
self.subtotal1Field.text = finalSubtotal1;
//Calculate product 2 within second Cell (Instead of Building more cells, I need to get information from User Input to create Cells and store information from them and add them to the calculation.
NSString *secondPrice = self.price1Field.text;
NSString *discount1P2 = self.discount1P2Field.text;
NSString *discount2P2 = self.discount2P2Field.text;
double subtotal2WithDiscount1;
double subtotal2WithDiscount2;
double secondPriceDouble = [secondPrice doubleValue];
double discount1P2Double = [discount1P2 doubleValue];
double discount2P2Double = [discount2P2 doubleValue];
double percentageDiscount1P2;
double percentageDiscount2P2;
double savings1P2;
double savings2P2;
percentageDiscount1P2 = discount1P2Double / 100;
percentageDiscount2P2 = discount2P2Double / 100;
savings1P2 = secondPriceDouble * percentageDiscount1P2;
subtotal2WithDiscount1 = secondPriceDouble - savings1P2;
savings2P2 = subtotal2WithDiscount1 * percentageDiscount2P2;
subtotal2WithDiscount2 = subtotal2WithDiscount1 - savings2P2;
NSString *finalSubtotal2 = [[NSString alloc] initWithFormat:#"$ %.02f", subtotal2WithDiscount2];
self.subtotal1Field.text = finalSubtotal2;
//Calculate Total
double totalAmount;
totalAmount = subtotal1WithDiscount2 + subtotal2WithDiscount2;
NSString *theTotal = [[NSString alloc] initWithFormat:#"$ %.02f", totalAmount];
self.totalField.text = theTotal;
//Calculate Money Saved
double moneySaved;
moneySaved = savings1P1 + savings2P1 + savings1P2 + savings2P2;
NSString *theMoneySaved = [[NSString alloc] initWithFormat:#"$ %.02f", moneySaved];
self.differenceField.text = theMoneySaved;
}
-(IBAction)removeKeyboard
{
[self.productField resignFirstResponder];
[self.price1Field resignFirstResponder];
[self.discount1P1Field resignFirstResponder];
[self.discount2P1Field resignFirstResponder];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (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.
}
- (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 == UIInterfaceOrientationPortrait);
}
#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 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ProductTableViewCell";
ProductCell *cell = (ProductCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLabel = [[NSBundle mainBundle] loadNibNamed:#"ProductTableViewCell" owner:self options:nil];
for (id currentObject in topLabel) {
if ([currentObject isKindOfClass:[ProductCell class]]) {
cell = (ProductCell *) currentObject;
break;
}
}
}
return cell;
}
#end
First of all, I assume you are using a custom UITableViewCell, which has a textfield as one of it's parameters.
I would suggest making an associated model with these cells, which has a parameter which stores the value of the textfield every time the user enters in new information. Something like:
#interface TableViewCellModel : NSObject {
NSString * _valueOfTextfield;
}
#property (nonatomic, retain) NSString * valueOfTextfield;
#end
#implementation TableViewCellModel
#synthesize valueOfTextfield = _valueOfTextfield;
#end
Then, when the textfield is pressed, and the user enters in the information and resigns the textfield, update the associated model of the cell to store the value of the textfield.
When you want to show the TOTAL of the textfields in all the cells, just look at each of the cells corresponding models, and check if the valueOfTextfield property is nil or not.
One warning though, if each of the custom UITableViewCells has their own textfield, you could have some issues with performance, as well as your touch events in the cells. You may want to look into having a singleton class which handles the textfields separately, and each one of the custom cells having a reference to this singleton class.
EDIT: I have played with some code, and I think I have what you want now. You'll need a few files, and this gets a bit long. They will be in .h, then .m pairs.
#interface TableViewModel : NSObject {
int _index;
NSString * _textFieldValue;
}
#property (nonatomic, assign) int index;
#property (nonatomic, retain) NSString * textFieldValue;
#end
#import "TableViewModel.h"
#implementation TableViewModel
#synthesize index = _index;
#synthesize textFieldValue = _textFieldValue;
#end
Then,
#import <UIKit/UIKit.h>
#class TableViewModel;
#interface TableViewCell : UITableViewCell {
IBOutlet UILabel * _label;
}
#property (nonatomic, retain) TableViewModel * tableViewModel;
- (IBAction)useTextField;
#end
#import "TableViewCell.h"
#import "TableViewModel.h"
#import "AppDelegate.h"
#implementation TableViewCell
- (void)dealloc; {
[super dealloc];
}
- (void)setTableViewModel:(TableViewModel *)tableViewModel; {
_label.text = [tableViewModel textFieldValue];
}
- (TableViewModel *)tableViewModel; {
return nil;
}
- (IBAction)useTextField; {
[[AppDelegate appDelegate] useTextFieldForIndex:[self.tableViewModel index]];
}
#end
and finally,
#import <UIKit/UIKit.h>
#class TableViewCell;
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate> {
int _index;
NSMutableArray * _items;
IBOutlet TableViewCell * _cell;
IBOutlet UITableView * _tableView;
IBOutlet UITextField * _textField;
}
- (void)useTextFieldForIndex:(int)aIndex;
#end
#import "ViewController.h"
#import "TableViewModel.h"
#import "TableViewCell.h"
#implementation ViewController
- (void)dealloc; {
[_items release];
[super dealloc];
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad; {
[super viewDidLoad];
// This is an example of how to fill in the _items array with Models
_items = [[NSMutableArray alloc] init];
TableViewModel * model = [[TableViewModel alloc] init];
model.textFieldValue = #"1";
model.index = 0;
[_items addObject:model];
[model release];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField; {
TableViewModel * model = [_items objectAtIndex:_index];
model.textFieldValue = [_textField.text retain];
[_items replaceObjectAtIndex:_index withObject:model];
[_tableView reloadData];
[_textField resignFirstResponder];
return YES;
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; {
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; {
return 100;//This should be whatever the height of the cell is!
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; {
return [_items count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; {
NSString * cellIdentifier = #"TableViewCell";
TableViewCell * cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil]; // hooks up _cell for us
cell = _cell;
}
cell.tableViewModel = ((TableViewModel *) [_items objectAtIndex:indexPath.row]);
return cell;
}
- (void)useTextFieldForIndex:(int)aIndex; {
_index = aIndex;
[_textField becomeFirstResponder];
}
#end
I assume that you know how to hook up the nibs, but if you need any more help, just ask. Also, you'll need to add these 2 methods to the AppDelegate.
+ (AppDelegate *)appDelegate; {
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void)useTextFieldForIndex:(int)aIndex; {
[self.viewController useTextFieldForIndex:aIndex];
}
This will allow the AppDelegate to pass the actions to have the textfield actions in only one view controller. It also should help with performance. To give a bit of explanation, _items holds all of the models which are used in the tableview. (Side note: when you want the user to add a new cell, all you need to do is add a new model to this array.)
The action: - (IBAction)useTextField; is attached to a clear button, and when it is pushed, it tells the AppDelegate to tell the ViewController to bring up the keyboard, and once you are done with it, you update the model as the index of the cell that was pressed, and reload the tableview.
You can then customize the cells the way you want, and make sure that you match the models as well.
To add up the values in the Models, you need only use:
double sum = 0;
for(TableViewCellModel * model in _items){
sum += [model.valueOfTextfield doubleValue];
}
Hope that helps!

connect a UINavigation Controller to table view within view based app

I have an view based app that has a main menu. In the main menu the user can select an option that presents a table that is populated from an SQLite database (BrowseView). That is all working fine, however from the table I want the user to be able to select a row of data that will present a detailed view and I cannot get the detailed view to work. Nothing happens.
I think the problem is that I havn't connected the detailed view to the UINavigation controller but I'm unsure on how to do this as I have tried everything that I can think of (which is probably not much as I'm new to programming).
Any help on this would be greatly appreicated. Here is my code.
In the appDelegate.h
#class ClubFindViewController;
#interface ClubFindAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
ClubFindViewController *viewController;
UINavigationController *navigationController;
NSMutableArray *clubArray;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet ClubFindViewController *viewController;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#property (nonatomic, retain) NSMutableArray *clubArray;
-(void) copyDatadaseIfNeeded;
-(NSString *) getDBPath;
#end
AppDelegate.m
#implementation ClubFindAppDelegate
#synthesize window;
#synthesize viewController;
#synthesize navigationController;
#synthesize clubArray;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self copyDatadaseIfNeeded];
NSMutableArray *tempArray = [[NSMutableArray alloc]init];
self.clubArray = tempArray;
[tempArray release];
[Clubs getInitialDataToDisplay:[self getDBPath]];
self.window.rootViewController = self.navigationController;
[self.window addSubview:navigationController.view];
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
return YES;
}
DetailView.h
#class Clubs;
#interface DetailViewController : UITableViewController {
IBOutlet UITableView *tableView;
Clubs *clubObj;
}
#property (nonatomic, retain) Clubs *clubObj;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#end
DetailView.m
#import "DetailViewController.h"
#import "Clubs.h"
#import "BrowseViewController.h"
#implementation DetailViewController
#synthesize clubObj;
#synthesize tableView;
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.title = clubObj.clubName;
[self.tableView reloadData];
}
-(NSString *)tableView:(UITableView *)tblView titleForHeaderInSection: (NSInteger)section {
NSString *sectionName = nil;
switch (section) {
case 0:
sectionName = [NSString stringWithFormat:#"Club"];
break;
case 1:
sectionName = [NSString stringWithFormat:#"Address"];
break;
}
return sectionName;
}
- (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 1;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tblView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
switch (indexPath.section) {
case 0:
cell.textLabel.text = clubObj.clubName;
break;
case 1:
cell.textLabel.text = clubObj.ClubAddress;
break;
}
return cell;
}
Updated code.
When you create your app and want to use UINavigationController you must in your AppDelegate tell that UINavigationController is your rootViewController.
self.window.rootViewController = self.navigationController;
When the Detail view will be pushed it will already (know) that it is inside the UINavigationController and so you don't need to create additional UINavigationController.
So delete UINavigationController *navigationController; from both .h and .m file.

NSArray causing memory leak when simulated low memory warning

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.