Question:
I'm trying to access a variable from the UITableView tableView:didSelectRowAtIndexPath: delegate method. The variable can be accessed from the data source methods, but when I try to access it from delegate methods such as this one, the app crashes.
I declare the variable in my .h file, and initialise it in .m file in the applicationDidFinishLaunching method. I haven't declared any accessors/mutators.
The weird thing is that the problem doesn't occur if I declare the variable like this:
helloWorld = #"Hello World!";
...but it does if I declare the variable like this:
helloWorld = [NSString stringWithFormat: #"Hello World!"];
Any ideas on what may be going on here? What am I missing?
Full code:
UntitledAppDelegate.h:
#import <UIKit/UIKit.h>
#interface UntitledAppDelegate : NSObject <UIApplicationDelegate, UITableViewDelegate, UITableViewDataSource> {
UIWindow *window;
NSString *helloWorld;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end
UntitledAppDelegate.m:
#import "UntitledAppDelegate.h"
#implementation UntitledAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
helloWorld = [NSString stringWithFormat: #"Hello World!"];
NSLog(#"helloWorld: %#", helloWorld); // As expected, logs "Hello World!" to console.
[window makeKeyAndVisible];
}
- (void)dealloc {
[window release];
[super dealloc];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
cell.textLabel.text = #"Row";
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"helloWorld: %#", helloWorld); // App crashes
}
#end
You need to retain your helloWorld instance variable. Try this:
helloWorld = [[NSString stringWithFormat: #"Hello World!"] retain];
It worked in the first instance because static strings are 'infinitely retained', and so are never deallocated. In the second, your instance variable is being released once the event loop runs. Retaining it will prevent this.
Related
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.
I am trying to load data from an array which was made by parsing json... I have the json data in the arrays for sure, but the table view methods are not even being called... I have the table view embedded in a regular uiviewcontroller, it compiles and everything the table just loads no data.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [transactions count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"any cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"any cell"] autorelease];
}
NSLog(#"got here");
cell.textLabel.text = [names objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [prices objectAtIndex:indexPath.row];
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
names = [[NSMutableArray alloc] init];
prices = [[NSMutableArray alloc] init];
[self requestTransactionData];
}
And here's my .h file...
#interface Controller1 : UIViewController
<UITableViewDelegate, UITableViewDataSource>
{
UIActivityIndicatorView *loadingIndicator;
IBOutlet UITableView *myTableView;
IBOutlet UILabel *totalLabel;
NSMutableArray *names;
NSArray *transactions;
NSMutableArray *prices;
}
#property(nonatomic, retain) IBOutlet UIActivityIndicatorView *loadingIndicator;
#property(nonatomic, retain) IBOutlet UILabel *totalLabel;
#property(nonatomic, retain) UITableView *myTableView;
I can't use ARC for this project. I also have it hooked up properly in the interface builder... These seems rather easy but for some reason I can't figure it out. Any suggestions?
Thanks
I don't see how you know the data source methods are not being called. You are not logging all of them. IfnumberOfRowsInSection is called before transactions is populated or is nil, it returns zero and that's the end of that.
Try calling reloadData using delayed performance so that it happens after transactions is populated. The table view doesn't magically know when your data is ready; you have to tell it.
call sef.tableView reloadData after names the prices were filled in requestTransactionData
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];
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've subclassed UITableView to create my own data retrieval system, as described in Apple's own Core Data tutorial, but I've hit a problem. The substitute cellForRowAtIndexPath method in my custom table never gets called, not even when I call reloadData or setNeedsDisplay. I've been hunting around for solutions to this one, and it seems that this error can be caused by a multitude of problems. However I've checked all the ones I can find and cannot see anything missing. Can anyone tell me what's likely to be wrong?
P.S. I know the UITableViewDataSource protocol needs to be in triangular brackets, but I couldn't find out how to show triangular brackets on the page without deleting the text between them.
H-File
#interface DataTable : UITableView <UITableViewDataSource> {
NSMutableArray *list;
}
#property (nonatomic, retain) NSMutableArray *list;
#end
M-File
#implementation DataTable
#synthesize list;
-(id) initWithFrame:(CGRect)rect Style:(UITableViewStyle)style{
if (self = [super initWithFrame:rect style:style]){
list = [[NSMutableArray alloc] init];
self.dataSource = self;
}
return self;
}
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [list count];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellID = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellID] autorelease];
}
CH *this = (CH *)[list objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [this.clusterName mutableCopy];
return cell;
}
-(void) dealloc{
[list release];
[super dealloc];
}
#end
Never mind, think I've found it. Basically, you can't set the datasource delegate in that part of the program. Need to do it from its owning view controller.