thanks for helping a novice. I'm trying to use the value of a Text Field in SecondViewController.xib to set up a settings string in WebService.m. I've included my code. When I execute the application, my NSLog from WebService give me an output of "The test IP is: (null)" The NSLog from SecondViewController.m is the value of the Text Field. How do you pass the strIP to WebService.m correctly? Using the example code would be very helpful.
SecondViewController.h:
#interface SecondViewController : UIViewController
{
UITextField *ipAdd;
NSString *strIP;
}
#property (nontoxic, retain) IBOutlet UITextField *ipAdd;
#property (nonatomic, retain) NSString *strIP;
-(IBAction)textchanged:(id)sender;
+(SecondViewController*)sharedIP;
SecondViewController.m:
-(IBAction)textchanged:(id)sender
{
strIP = ipAdd.text;
NSLog(#"the string in the text field is: %#", strIP);
}
+(SecondViewController*)sharedIP
{
static SecondViewController *sharedIP = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedIP = [[SecondViewController alloc] init];
});
return sharedIP;
}
WebService.m:
SecondViewController *IP = [SecondViewController sharedIP] ;
NSLog(#"The test IP is: %#", IP.strIP);
If you want to modify a value in a block, you need to use the __block type specifier on the variable type (sharedIP) so modifications in the block affect the value outside the block.
Also: every time you call +sharedIP, you set the value of the static to nil and only reset it the first time. Your design seems a bit odd... I'm not sure why you would ever want a static instance of a view controller, but I don't know what you're after. If you just want persistent data, see NSUserDefaults (which is cool because it persists over restarts). It might be better to create a static instance of a data container to persist your string value.
Good luck.
Check delegate of your textField that should be your SecondViewController.
Try the print the same value before you return. e.g. :
+(SecondViewController*)sharedIP
{
static SecondViewController *sharedIP = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedIP = [[SecondViewController alloc] init];
});
**NSLog(#"The test IP is: %#", sharedIP.strIP);**
return sharedIP;
}
Might help
Cheers
Related
It's me again, I've been struggling with this for the past hour and a half and can't seem to find a good way of implementing this. I'm basically trying to display results on a label on clicking of a button. (Just starting out with xcode so I'm not sure if that's the right term for that action). Anyway, here's my code and the method on my controller: I have
#interface Match : NSObject{
}
#property NSInteger *Id;
#property NSString *fighter1, *fighter2;
- (id) initWithWCFId:(NSInteger)matchId bracketId:(NSInteger)bracketId;
#end
#implementation Match
- (id) initWithWCFId:(NSInteger)matchId bracketId:(NSInteger)bracketId{
self = [self init];
if(self){
self.Id = &(matchId);
self.fighter1 = #"Person 1";
self.fighter2 = #"Person 2";
}
return self;
}
#end
--- controller ---
#interface ViewController : UIViewController{
/*IBOutlet UITextField *txtFieldBracketId;
IBOutlet UITextField *txtFieldMatchId;*/
}
#property (weak, nonatomic) IBOutlet UITextField *txtFieldBracketId;
#property (weak, nonatomic) IBOutlet UITextField *txtFieldMatchId;
- (IBAction)btnSubmit:(id)sender;
#end
--- implementation
- (IBAction)btnSubmit:(id)sender {
#autoreleasepool {
Match *match = [[Match alloc]initWithWCFId:[_txtFieldMatchId.text integerValue] bracketId:[_txtFieldBracketId.text integerValue]];
self.lblMatchId.text = [[NSString alloc] initWithString:[NSNumber numberWithInt:match.Id]];
self.lblFighter1.text = [[NSString alloc] initWithString:match.fighter1];
self.lblFighter2.text = [[NSString alloc] initWithString:match.fighter2];
}
}
I basically have two text boxes.
Now when I click the button it'll get the value for those text boxes and then displays the data it got based off of those inputs. It'll then display the three following data:
Id, Fighter1 and Fighter2.
So what's happening is, when I click the button, the whole thing stops and gives me this error:
NSInvalidArgumentException', reason: '-[__NSCFNumber length]:
unrecognized selector sent to instance 0x74656e0'
* First throw call stack: (0x1c90012 0x10cde7e 0x1d1b4bd 0x1c7fbbc 0x1c7f94e 0xae4841 0x2891 0x10e1705 0x18920 0x188b8 0xd9671 0xd9bcf
0xd8d38 0x4833f 0x48552 0x263aa 0x17cf8 0x1bebdf9 0x1bebad0 0x1c05bf5
0x1c05962 0x1c36bb6 0x1c35f44 0x1c35e1b 0x1bea7e3 0x1bea668 0x1565c
0x23dd 0x2305) libc++abi.dylib: terminate called throwing an exception
Now I'm not sure if 1. The way I designed my class is correct, using "NSInteger" for the property id. or
2. Assigning the Id integer to string (edit box) is wrong.
Two things:
The property should not be pointer type, so it should be #property NSInteger Id; and in init it should be just self.Id = matchId;
Make it to string by using [NSString stringWithFormat:#"%d", match.Id]
In addition to the issues with your Id property, the crash is coming from this:
self.lblMatchId.text = [[NSString alloc] initWithString:[NSNumber numberWithInt:match.Id]];
You are trying to pass an NSNumber object as the argument to the initWithString: method. But this method expects an NSString value, not an NSNumber.
Update the three lines to:
self.lblMatchId.text = [[NSString alloc] initWithFormat:#"%d", match.Id];
self.lblFighter1.text = match.fighter1;
self.lblFighter2.text = match.fighter2;
I'm assuming match.fighter1 and match.fighter2 are NSString properties.
I'm trying to share a string between two views on my iPhone project. It currently works if I use the actual #"something here" for the string, but if I want to use something like label.text, it doesn't even though it is still a string.
I'll show you what I have to make it clearer.
First View: Info_ViewController.h
#import <UIKit/UIKit.h>
#interface Info_ViewController : UIViewController {
IBOutlet UITextField *locationField;
}
#property (nonatomic, retain) NSString *locationString;
+ (id)sharedInfoVC;
#end
First View: Info_ViewController.m
#import "Info_ViewController.h"
static Info_ViewController *sharedInfoVC = nil;
#implementation Info_ViewController
#synthesize locationString;
#pragma mark Singleton Methods
+ (id)sharedInfoVC {
#synchronized(self) {
if (sharedInfoVC == nil)
sharedInfoVC = [[self alloc] init];
}
return sharedInfoVC;
}
- (id)init {
if (self = [super init]) {
locationString = [[NSString alloc] initWithString:locationField.text]; //This is there part I mentioned earlier, when using #"something" instead of locationField.text works.
}
return self;
}
Second View: Confirm_ViewController.m
#import "Confirm_ViewController.h"
#import "Info_ViewController.h"
#implementation Confirm_ViewController
- (IBAction)buttonZ:(id)sender
{
Info_ViewController *infoVCmanager = [Info_ViewController sharedInfoVC];
locationLabel.text = infoVCmanager.locationString;
}
I put it under a button for now, but it will eventually be under viewDidLoad.
If you replace locationField.text with a string (#"blahblahblah") it won't crash and works.
When it crashes I get the error: Program received signal: "SIGABRT"
EDIT: I tried changing
initWithString:locationField.text
to
initWithFormat:#"%#",locationField.text
and now it my label in the second view prints "(NULL)"
Thanks for taking the time to give advice, I really appreciate it.
It is an error to pass nil as the format string to -[NSString initWithString].
So how are you passing nil? You actually have two instances of Info_ViewController. You have the one instance which is the normal part of your app, and then you also have a second instance which is your "singleton" (which really isn't a singleton any more).
So in your "singleton" instance, the UITextField is nil (and will always be nil) and so locationField.text is nil and you are passing that to initWithString:, which is a crash. In fact the "singleton" isn't even fully baked as view controller's go.
If you want a singleton to share data elsewhere in your app, it really should not be a Info_ViewController or any type of view controller. It should be of some other class that you use to manage your data. I would create another class and implement that as a singleton.
Hope that helps you understand what's happening here.
Pre-pend "self." to your location string.
self.locationString = [[NSString alloc] initWithString:locationField.text];
From what I understand of your code, you have got the value for locationString when you from the textfield when you initialize the viewController. At this point of time, your textfield would not be visible. After it becomes visible and you enter something, you don't have the code to store it to locationString.
What you should do is wait for Info_ViewController object to be initialized and displayed. Then on the press of some button or some other event, assign locationLabel.text from the locationString or even directly from locationField.text.
I would provide code, but I have no clue as to how you are structuring this. If you still need help, please provide the details.
Sorry I'm still a noob and just learning to program as I go and want to start out on the right foot by learning good design up front. I am using the CLLocationManager and MKReverseGecoder to get my location. In my MKReverseGecoderDelegate method, I create my annotation to show on the MKMapView. In my callout, I use a detail disclosure indicator to bring up another UITableView that displays your current address nicely as opposed to looking at the little black callout bubble.
What is a good way for my DetailViewController (the UITableView) to get the data? Do I have my first class have ivars for address, state, zipcode. In my MKReverseGecoderDelegate, set those ivars when I get that information. (The reason I would think I would need ivars is because my method to get that information in the MKReverseGeocoderDelegate is separate from the displayDetailViewController). And then do I have my DetailViewController have those same values, and when I go to display the DetailViewController, set those same variables? It seems redundant.
Any help would be greatly appreciated. Thanks!
One option
Declare custom class inheriting NSObject like
#interface YourClassName : NSObject
{
NSString *address;
NSString *state;
NSString *zipcode;
}
#property(nonatomic, retain) NSString *address;
#property(nonatomic, retain) NSString *state;
#property(nonatomic, retain) NSString *zipcode;
#end
#implementation YourClassName
#synthesize address,state,zipcode;
-(void)dealloc
{
[super dealloc];
[address release];
[state release];
[zipcode release];
}
#end
//Create object of YourClassName and set values
YourClassName *objYourClassName = [[YourClassName alloc] init];
objYourClassName.address = #"YourValue";
objYourClassName.state = #"YourValue";
objYourClassName.zipcode = #"YourValue";
Pass this object to your DetailViewController by one method after creating method like
-(void)setDetailsForDetailViewController:(YourClassName*)pObjYourClassName
{
//self.objOfYourClassName is property declared in your detailviewcontroller.
self.objOfYourClassName = pObjYourClassName; //You can use self.objOfYourClassName to set values in TableViewController.
}
If you stuck any where let me know I would be glad to help you fix that.
If you are doing the reverse geocoding on demand, initialize the DetailViewController with the coordinate of the annotation. Something like this:
- (id)initWithCoordinate:(CLLocation*)location {
if (self = [super initWithNibName:#"DetailController" bundle:nil]) {
self.location = location;
}
return self;
}
This is a common pattern to create the controllers, because it makes it clear for the controller's user that the controller depends on a location parameter. The other alternatives (global variables, or a singleton) are not so clean because they hide information and make the controller harder to understand and unit test.
Then let the controller launch an asynchronous task to do the geocoding, set itself as delegate, and present the information when it's done.
I am currently learning to program on iPhone.
I currently have a small test app with a mock up keypad, so I have a few buttons that when pressed should place some text in a label.
I then take whats in the label and use it to open an ABUNKnownPersonControllerView for use there. Just a basic app to get used to a few things in the iPhone.
Where I am having trouble seems to be with working with the string used to display what I want in the label. I get the EXC_BAD_ACCESS error so I've re-read the memory management docs but I cant find anywhere that there should be a memory leak.
So I think my problem is I am incorrectly working with the String. My code is as follows:
//Header file
#interface KeyPadViewController : UIViewController <ABUnknownPersonViewControllerDelegate>{
IBOutlet UILabel *phoneNumber;
//NSString *number;
}
#property (nonatomic, retain) UILabel *phoneNumber;
//#property (nonatomic, retain) NSString *number;
-(IBAction)addNum1;
-(IBAction)addNum2;
-(IBAction)showUnknownPersonViewController;
//Implementation file
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = NSLocalizedString(#"Keypad",#"Keypad");
//self.number = #"";
self.phoneNumber.text = #"";
}
-(IBAction)addNum1{
//self.number = [self.number stringByAppendingString:#"1"];
self.phoneNumber.text = [self.phoneNumber.text stringByAppendingString:#"1"];
}
-(IBAction)addNum2{
//self.number = [self.number stringByAppendingString:#"2"];
self.phoneNumber.text = [self.phoneNumber.text stringByAppendingString:#"2"];
}
And the showUnknownPerson view in the implementation file:
-(void)showUnknownPersonViewController
{
//NSString *contactNum = self.phoneNumber.text;
ABRecordRef aContact = ABPersonCreate();
CFErrorRef anError = NULL;
ABMultiValueRef email = ABMultiValueCreateMutable(kABPersonPhoneProperty);
bool didAdd = ABMultiValueAddValueAndLabel(email, self.phoneNumber.text, kABPersonPhoneMobileLabel, NULL);
if (didAdd == YES)
{
ABRecordSetValue(aContact, kABPersonPhoneProperty, email, &anError);
if (anError == NULL)
{
ABUnknownPersonViewController *picker = [[ABUnknownPersonViewController alloc] init];
picker.unknownPersonViewDelegate = self;
picker.displayedPerson = aContact;
picker.allowsAddingToAddressBook = YES;
picker.allowsActions = YES;
[self.navigationController pushViewController:picker animated:YES];
[picker release];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not create unknown user"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
CFRelease(email);
CFRelease(aContact);
}
And the dealloc:
- (void)dealloc
{
[phoneNumber release]; //#synthesize
// [number release]; //#synthesize
[super dealloc];
}
I have tried using a Mutable string but have the same error, I've also tried not releasing the UILabel and the NSString and have the same issue.
When my app loads if I press one button and one character appears in the label, if I then use the showUnknowPersonViewController everything works fine, have if I press button 1 and then button 2 so that I know have two characters in the UILabel ("12") and select to use the showUnknownPersonViewcontroller then I get the crash.
*** -[CFString release]: message sent to deallocated instance 0x1226a0
However I don't understand where it could be getting deallocated?
This is probably something basic I've missed in the tutorials but I cant figure it out, can anyone see what I might be doing wrong?
EDIT: Just to add if the self.phoneNumber.text (UILabel) returns either empty or one character it works fine however if its 2 or more I get the crash.
EDIT 2: I have removed my NSString from the code completely and I still have the same issue, so it looks like its the UILabel thats causing the issue? See code changed above.
I think the problem may be in the line:
self.phoneNumber.text = number;
You are setting self.phoneNumber.text to the same string as number, not to a copy or to a newly created string with the same content. Thus, in your addNum methods, the setter of one property releases the string when it resets its value. The second property is still pointing to the released string. When the setter of the second property is called, it tries to release that string again. Hence, the error.
If this is the problem, it could be solved by rewriting the line as:
self.phoneNumber.text = #"";
Although, even this is probably not necessary. All instance variables are initiated to nil, and it would not be necessary to give it an initial value.
I guess your NSString variable number is causing the problem. It seems the values you assign to that variable are not retained anywhere. As you are using self to access the variable phoneNumber, you need use that for accessing the number too. Using self calls the setter for the particular property where the property is actually retained.
I believe the property might also need to be declared as an IBOutlet, and, if so, that could be causing the problem.
#property (nonatomic, retain) IBOutlet UILabel *phoneNumber;
It seems you never allocate number. What is the initial value of number?
To my mind, you should allocate a string in your viewDidLoad
I'm new to Objective-C and iPhone SDK development. I want to call a method in the same class:
- (void) setFilePath:(NSString *) p
{
[self methodCall];
}
- (void) methodCall
{
fileContent.text = #"Test"; //fileContent is a UITextView
}
If the property "filePath" is set, the method "setFilePath" is called. Then the UITextView, created in IB, should display the text. But that doesn't work ...
If I call the method directly via button in IB, then the UITextView changes his content successfully:
- (IBAction) clickButton
{
fileContent.text = #"Test";
}
What could be the problem?
Thanks for your answers!
EDIT 2: I solved the problem by setting "filePath" after pushing the view:
- (IBAction) showFileContent {
FileContentsViewController *fileContentsViewController = [[FileContentsViewController alloc] init];
[self.navigationController pushViewController:fileContentsViewController animated:YES];
fileContentsViewController.filePath = self.filePath;
fileContentsViewController.title = [NSString stringWithFormat:#"Content from von %#", [filePath lastPathComponent]];
[fileContentsViewController release];
}
EDIT 1: Here's the code of my interface:
#interface FileContentsViewController : UIViewController {
NSString *filePath;
UITextView *fileContent;
}
- (void) methodCall;
#property (nonatomic, retain) NSString *filePath;
#property (nonatomic, retain) IBOutlet UITextView *fileContent;
#end
... and here's the code of the implementation:
#import "FileContentsViewController.h"
#implementation FileContentsViewController
#synthesize filePath;
#synthesize fileContent;
- (void) setFilePath:(NSString *) p
{
NSLog(#"setFilePath executed!");
[self methodCall];
}
- (void) methodCall
{
fileContent.text = #"Test"; // UITextView
}
// some standard methods
#end
... and finally the code of the method that sets "filePath":
- (IBAction) showFileContent {
FileContentsViewController *fileContentsViewController = [[FileContentsViewController alloc] init];
fileContentsViewController.filePath = self.filePath;
fileContentsViewController.title = [NSString stringWithFormat:#"Content from von %#", [filePath lastPathComponent]];
[self.navigationController pushViewController:fileContentsViewController animated:YES];
[fileContentsViewController release];
}
What it looks like is that the fileContentsViewController created in -showFileContent doesn't have anything assigned to its FileContentsViewController.fileContent (or, at least, fileContent doesn't point to a UITextView that gets displayed) when fileContentsViewController.filePath is set.
You set filePath immediately after creating fileContentsViewController. If FileContentsViewController's -init doesn't create an appropriate fileContent, then when -setFilePath: is called from -showFileContent, there's no fileContent to set the text of. If fileContentsViewController is a typical view controller, fileContent won't exist until fileContentsViewController is loaded, which (I believe) happens during -pushViewController:animated.
One fix is to override -setFileContent to set fileContent.text as appropriate:
-(void)setFileContent:(UITextView*)fileContentView {
if (fileContent != fileContentView) {
[fileContent release];
fileContent = [fileContentView retain];
if (self.filePath) { // if file path is not nil
fileContent.text = ...;
}
}
}
Another other fix is to ensure you only set filePath when fileContent exists, but this is more brittle. A third is to set filePath after you push fileContentsViewController.
The way you would discover the cause during debugging is to check two things: execution ("Is the code I'm expecting to be executed ever reached?") and data ("Do the variables hold the values I expect?"). Set breakpoints in -showFileContent and -methodCall so you know that the methods are being called (which would be one reason for failure). If execution makes it into -methodCall, the problem must be something else. From there, examine the values of the variables used in -methodCall and you'll discover fileContent is either nil or not the same fileContent that shows up later.
Have you checked that fileContent has been set up at the time setFilePath is called? If you're trying to set things up at start up then it's possible that you're making calls before the views have been loaded (which the OS delays until the last possible moment).
You can force views to load by calling [self view] just before you try to access any of your Interface Builder views (NB don't call loadView - that doesn't do what you'd think).
If the problem is that setFilePath: is not called that I would guess that your code looks like
filePath = #"some value";
when it should be
self.filePath = #"some value";
When using #property you need to use self.filePath to call the methods, otherwise you will just access the ivar directly.
How have you define filePath property ?
I think that it is the problem...