Going Crazy with UITextField Input in a Very Simple App - objective-c

I have a PromoCodeViewController which is triggered using the following code:
#implementation Demo_WebServiceCallingUsingiOSAppDelegate
#synthesize window = _window,promoCodeController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.promoCodeController = [[PromoCodeViewController alloc] init];
[self.window addSubview:self.promoCodeController.view];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
The PromoCodeViewController contains the UITextField and I implement the UITextFieldDelegate for the PromoCodeViewController as shown:
#interface PromoCodeViewController : UIViewController<UITextFieldDelegate>
{
IBOutlet UITextField *promoCodeTextField;
}
#property (nonatomic,retain) IBOutlet UITextField *promoCodeTextField;
#end
I implement the textFieldShouldReturn method as shown:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return TRUE;
}
I have even setup the PromoCodeViewController to be the delegate for the UITextField events. When I start typing in the TextBox it throws "Program received signal. EXC_BAD_ACCESS". It happens when I type second character in the UITextField. What am I doing wrong?
UPDATE 1:
The error comes on the following section:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([Demo_WebServiceCallingUsingiOSAppDelegate class]));
}
}

It looks as though you're missing an #synthesize statement for the promoCodeTextField property. Add the following to your Demo_WebServiceCallingUsingiOSAppDelegate class (which by the way, might benefit from renaming):
#synthesize promoCodeTextField = _promoCodeTextField;
Without the #synthesize statement, Xcode's Nib File Editor will directly set the instance variable instead of calling the accessor methods (since they don't exist); as a consequence the text field is never retained.

What object is the delegate of the textfield? Objects do not usually retain their delegates (since the delegate is often retaining a reference to the object, and retain-cycles lead to memory leaks).
My hunch is that your text field's delegate is defined and attached in your nib, but isn't being retained anywhere, so shortly after the nib is loaded the delegate is deallocated. As soon as the text field attempts to do anything with the delegate, it crashes.
If so, make sure something (usually the application delegate) is retaining a reference to whatever object is serving as the delegate of your text field.

Related

Unable to set content in NSPopover

I'm showing an NSPopover in an NSView, originating from a point on an NSBezierPath. I'm able to show the popover without a problem, but I can't seem to set the string value of the two text fields in it. The popover and the content view are both a custom subclass of NSPopover and NSViewController, respectively. The NSPopover subclass is also the NSPopover's delegate, although I don't implement any delegate methods, so I'm not sure I even need to do that.
Here is my subclass of NSViewController:
#import <Cocoa/Cocoa.h>
#interface WeightPopoverViewController : NSViewController
#end
#import "WeightPopoverViewController.h"
#interface WeightPopoverViewController ()
#end
#implementation WeightPopoverViewController
- (id)init {
self = [super initWithNibName:#"WeightPopoverViewController" bundle:nil];
if (self) {
}
return self;
}
#end
And my subclass of NSPopover:
#import <Cocoa/Cocoa.h>
#interface WeightPopoverController : NSPopover <NSPopoverDelegate> {
NSTextField *dateLabel;
NSTextField *weightLabel;
}
#property (strong) IBOutlet NSTextField *dateLabel;
#property (strong) IBOutlet NSTextField *weightLabel;
#end
#import "WeightPopoverController.h"
#implementation WeightPopoverController
#synthesize weightLabel;
#synthesize dateLabel;
#end
This is the code in my NSView subclass that opens up the popover:
#interface WeightGraphViewController () {
WeightPopoverController *popover;
WeightPopoverViewController *vc;
}
...
-(void)mouseEntered:(NSEvent *)theEvent {
// initialize the popover and its view controller
vc = [[WeightPopoverViewController alloc] init];
popover = [[WeightPopoverController alloc] init];
// configure popover
[popover setContentViewController:vc];
[popover setDelegate:popover];
[popover setAnimates:NO];
// set labels
for (id key in (id)[theEvent userData]) {
[popover.weightLabel setStringValue:[(NSDictionary*)[theEvent userData] objectForKey:key]];
[popover.dateLabel setStringValue:key];
}
// set the location
(redacted, irrelevant)
// show popover
[popover showRelativeToRect:rect ofView:[self window].contentView preferredEdge:NSMaxYEdge];
}
-(void)mouseExited:(NSEvent *)theEvent {
[popover close];
popover = nil;
}
In WeightPopoverViewController.xib, I've set the File's Owner to WeightPopoverViewController and connected the view to the custom NSView. In this xib I also have an Object set to WeightPopoverController with the dateLabel and weightLabel connected to their text fields and the contentViewController set to File's Owner.
I think where I am going wrong is likely related to how I have configured my class / instance variables for the NSPopover, but from the research I've done and documentation I've read I can't seem to crack where I've gone wrong. Any help would be appreciated.
UPDATE:
I removed the NSPopover subclass from code and from IB. I put my outlets in my NSViewController and connected them in IB. However, I'm still not able to set the string values. The following won't compile with the error "Property 'weightLabel' not found on object of type NSPopover*'".
#interface WeightGraphViewController () {
NSPopover *popover;
...
}
-(void)mouseEntered:(NSEvent *)theEvent {
vc = [[WeightPopoverViewController alloc] init];
popover = [[NSPopover alloc] init];
[popover setContentViewController:vc];
[popover.dateLabel setStringValue:#"test"];
}
I have the property definition exactly as I had it in my NSPopover subclass, but now in my NSViewController. This is actually what I had before, and since I wasn't able to set the properties from the NSViewController, I figured I needed to do it through a subclass of NSPopover. This is why I thought I am having an issue with how I have configured my class / instance variables.
You seem to be creating two popovers, one in code (popover = [[WeightPopoverController alloc] init]) and one in Interface Builder (In this xib I also have an Object set to WeightPopoverController). Have a think about what you’re trying to achieve.
I would also advise against subclassing NSPopover. I believe this is causing confusion and is unnecessary. Instead, put the outlets to your dateLabel and weightLabel in the popover’s content view controller.
I've experienced something that I think is similar. The root problem is that the "outlets" connecting your view (XIB) to your controller are not initialized until after the view has been displayed. If the controller tries to set properties on any UI controls in the view before the popover has been opened, those changes are ignored (since all the controls will be nil).
Luckily, there's an easy solution (as mentioned in this answer): just invoke the view getter on your controller, and it will force the view to initialize sooner.
In other words:
popover = [NSPopover new];
myController = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil];
popover.contentViewController = myController;
[myController view]; // force view to initialize
...set some values on myController... // works because view is now loaded
[popover showRelativeToRect: ...];

Calling a method from another class - NSTextView not loading data

I have 3 classes:
LocalRoom.m
- (void) handleNewConnection:(Connection*)connection {
NSString* number = #"10";
AppDelegate *theAppDelegate = [[NSApplication sharedApplication] delegate];
[theAppDelegate connectionNumber:number currentZoneName:#"Zone1";
}
Triggers ----->
AppDelegate.h
MyTableController *tableController;
AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
tableController = [[MyTableController alloc] init];
}
- (void)connectionNumber:(NSString *)number zoneName:(NSString *)currentZoneName {
if ([currentZoneName isEqualToString:zoneName1]) {
[tableController changeConnections:number withRowNumber:0];
}
}
Triggers ----->
MyTableController.h
#interface MyTableController : NSControl {
IBOutlet NSTextField *zoneNameTextField;
}
#property (retain) NSTableView * idTableView;
- (void) changeConnections:(NSString *)number withRowNumber:(int)rowNumber;
MyTableController.m
- (void) changeConnections:(NSString *) number withRowNumber:(int) rowNumber{
NSRunAlertPanel([NSString stringWithFormat:#"%d", rowNumber], number, #"", #"", #"");
NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier: #"Connections"];
[self tableView:idTableView setObjectValue:number forTableColumn:tableColumn row:rowNumber];
[idTableView reloadData];
}
My NSRunAlertPanel is coming up with the correct values, but my NSTextView is not changing. When I put the "edit" code for the NSTableView in an IBAction of MyTableController.m, it works. I checked if idTableView was NULL, and it was. Which is weird. So, I have a feeling something odd is happening when changeConnections is run by the AppDelegate. I had a similar problem with a nstextfield not returning data. The fix was to initialize it by AppDelegate *theAppDelegate = [[NSApplication sharedApplication] delegate]; Is there something similar in this case?
You might wonder why I'm just not calling MyTableController from LocalRoom, but "zoneName1" is set and found in the app delegate.
Thanks in advance!
You haven't shown all the code, but idTableView is declared as a #property of MyTableController, but not as an IBOutlet.
That is idTableView being NULL strongly suggests that the idTableView property is never set to the appropriate table view. No IBOutlet for it means it can't be connected in the NIB/XIB. So the fix is probably to make it an outlet and connect it in the NIB.
Obviously, [idTableView reloadData] has no effect if idTableView is NULL.
I assume that your NIB does set MyTableController as its table data delegate, which is why the direct editing would work.
Further, although I don't think this is related to your problem, for best practice it doesn't make sense to manufacture a new NSTableColumn to work on your existing table -- you should be using [idTableView tableColumnWithIdentifier: #"Connections"] instead.

Pressing a button on a custom keyboard is resulting in "Unrecognized selector being sent to instance error"

I know there are plenty of other questions addressing the same problem, but since I'm using a custom keyboard, I thought my problem would be slightly different.
This is the specific error:
-[EquationTextField element1Pressed:]: unrecognized selector sent to instance 0x4b68ee0
2012-01-02 12:23:44.630 rowQuiz[20975:207] Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[EquationTextField element1Pressed:]: unrecognized selector sent to instance 0x4b68ee0'
I have a view controller, quizController. Inside quizController is a custom view, textField (added through interface builder).
When textField is tapped, another custom view, formulaKeyboard, pops up as its keyboard. When a button on the keyboard is pressed, method element1Pressed: is called, and the error described above appears.
Some other questions say that there must be a problem with the retain count, so I tried retaining and releasing quizController in the app delegate, which didn't solve the problem.
It is also possible that I hooked up something incorrectly in Interface Builder; For the custom keyboard, File's owner and the main view are set to class elementKeyboard. For quizController, File's owner is set to quizController and hooked up to it's view.
Below is the code of the textField's class.
EquationTextField.h
#import <UIKit/UIKit.h>
#import "FormulaKeyboard.h"
#interface EquationTextField : UIView <KeyInput> {
FormulaKeyboard *keyboard;
NSString *lastElement;
}
#property (readwrite, retain) UIView *inputView;
#end
EquationTextField.m
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.userInteractionEnabled = YES;
[self addGestureRecognizer:
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(becomeFirstResponder)]];
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"FormulaKeyboard" owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[FormulaKeyboard class]])
keyboard = (FormulaKeyboard *)object;
}
self.inputView = keyboard;
keyboard.delegate = self;
}
return self;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark -- KeyInput Protocol Methods
- (void)addElement:(NSString *)elementName {
}
- (void)addCharge:(NSString *)chargeIncrease {
}
- (void) addState:(NSString *)stateName {
}
- (void)deleteCharacter {
}
- (void)dealloc
{
[super dealloc];
}
formulaKeyboard.h
#import <UIKit/UIKit.h>
#protocol KeyInput <UITextInputTraits>
- (void) addElement:(NSString*) elementName;
- (void) addCharge:(NSString*) chargeIncrease;
- (void) addState:(NSString*) stateName;
- (void) deleteCharacter;
#end
#interface FormulaKeyboard : UIView {
id <KeyInput> delegate;
}
#property (nonatomic, retain) id <KeyInput> delegate;
-(IBAction) element1Pressed:(id)sender;
-(IBAction) element2Pressed:(id)sender;
-(IBAction) element3Pressed:(id)sender;
-(IBAction) element4Pressed:(id)sender;
-(IBAction) element5Pressed:(id)sender;
-(IBAction) element6Pressed:(id)sender;
-(IBAction) chargePlusPressed:(id)sender;
-(IBAction) chargeMinusPressed:(id)sender;
-(IBAction) solidSatePressed:(id)sender;
-(IBAction) liquidStatePressed:(id)sender;
-(IBAction) gasStatePressed:(id)sender;
#end
formulaKeyboard.m
- (IBAction)element1Pressed:(id)sender {
[delegate addElement:#"Na"];
}
- (void)element2Pressed:(id)sender {
[delegate addElement:#"N"];
}
- (void)element3Pressed:(id)sender {
[delegate addElement:#"O"];
}
- (void)element4Pressed:(id)sender {
}
- (void)element5Pressed:(id)sender {
}
- (void)element6Pressed:(id)sender {
}
appDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
quizController = [[QuizController alloc] initWithNibName:#"QuizController" bundle:nil];
[self.window addSubview:quizController.view];
[self.window makeKeyAndVisible];
return YES;
}
- (void)dealloc
{
[_window release];
[quizController release];
[super dealloc];
}
The action of the keyboard's buttons are pointing to the wrong place. You've probably got them wired to File's Owner inside the FormulaKeyboard nib when they should be wired to the FormulaKeyboard object you're creating inside the nib.
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"FormulaKeyboard" owner:self options:nil];
That's being called from EquationTextField, so self would be your instance of EquationTextField. If you're keyboard's targets are pointing there, that's why you get the unrecognized selector exception.
What's happening here is a method called element1Pressed: is being sent to an instance of EquationTextField. You need to actually add the method to the class for it to work. Right now, it's sending the message to the field class, but there's no matching method, so it's throwing an error.
Also, I can't be completely sure about this, since you haven't posted the whole code and/or NIB info, but it seems that you may be going about this the wrong way. You should be using a view controller to handle everything, rather than a custom text field class. I notice that you haven't posted any code for the QuizController class. Once you do so, I may be able to give you more advice.
EDIT: Now that you've posted more code, I think I see the problem. You want the FormulaKeyboard instance to receive the event, but the event is linked to the EquationTextField instance instead. Make sure you wire it to an instance of FormulaKeyboard instead.
On the other hand, it seems that you may not have an instance of FormulaKeyboard in the NIB at all. Add an NSLog after keyboard = (FormulaKeyboard *)object to test if keyboard is ever actually assigned a value. If the NSLog doesn't fire, double-check that you've actually added an instance of FormulaKeyboard to the NIB.

Where is your Application Delegate set and who initializes its window and viewController properties?

I have a very newbie question about IOS apps... If I create a new View Based Application called TestForStackOverflow, Xcode automatically creates code like this for the TestForStackOverflowAppDelegate.h:
#class TestForStackOverflowViewController;
#interface TestForStackOverflowAppDelegate : NSObject <UIApplicationDelegate>
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet TestForStackOverflowViewController *viewController;
#end
and the following in TestForStackOverflowAppDelegate.m:
#import "TestForStackOverflowAppDelegate.h"
#import "TestForStackOverflowViewController.h"
#implementation TestForStackOverflowAppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
[...]
Here come my questions:
1) where is the TestForStackOverflowAppDelegate class set as delegate for the current application? Is it done "automagically"? I've seen that the main.m source file contains only
the following code:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
Shouldn't it set the application delegate class in the fourth parameter of the UIApplicationMain function invocation?
2) where are the window and viewController properties of TestForStackOverflowAppDelegate class being set?
3) this may be trivial, but why do we have synthesize window = _window, without having an instance variable called _window in TestForStackOverflowAppDelegate interface? I've seen that you can declare #properties without having the corresponding iVars in the class interfaces (maybe they are automatically created by the compiler), but is it a good practice or should you always create the corresponding iVars in your classes?
Excuse me for the very long message, I just hope I've not written a too obvious question since here in Italy is late night and I'm very tired.. but when these questions come into my head, I can't wait for the solution :)
How is the app delegate class loaded?
In your -info.plist file there is a key named "Main nib file base name" and has a view something like MainWindow.xib. In that xib file, there's a file's owner proxy object and it has a delegate set to the app delegate class.
Open that XIB in the designer. Notice the File's Owner object at the top, context click on it and look at the delegate object. Now look at the objects in the design below the (XCode 4) line - you should see the app delegate object there.
Where is the window and viewController for the appDelegate set?
Look in the designer and context click on the app delegate object below the (XCode 4) line. The window and viewController target is tied there.
_window iVar
It's automatically provided for you. Not clear to me whether it's inherited or generated by the compiler.
Here's more on _ iVars:
Why rename synthesized properties in iOS with leading underscores?
If you chose to do the equivalent in code:
remove plist xib reference
main.m: pass name of delegate
int main(int argc, char *argv[])
{
int retVal = UIApplicationMain(argc, argv, nil, #"HelloViewAppDelegate");
In app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
CGRect screenBounds = [[UIScreen mainScreen] applicationFrame];
CGRect windowBounds = screenBounds;
windowBounds.origin.y = 0.0;
// init window
[self setWindow: [[UIWindow alloc] initWithFrame:screenBounds]];
// init view controller
_mainViewController = [[MainViewController alloc] init];
[[self window] addSubview:[_mainViewController view]];
[self.window makeKeyAndVisible];
return YES;
}
1) To be simple, when UIApplicationMain() is called,
you application .plist is found,
the key "Main nib file base name" is looked up,
this XIB is opened,
The object that supports the UIApplicationDelegate protocol is instantiated and used as the application delegate
2) They are set in the XIB, you can view them if you
open the main XIB,
select the AppDelegate object,
open the Connections inspector for this object,
look into the Outlets section
3) You are right, they are created automatically by the compiler. It makes the code more readable by having less line, and more easy to refactor having only one place the property is declared.
Hope it helps!

Setting the initial value of a UILABEL

I'm trying to create a simple Quiz app (I'm a beginner), when I launch the app I want a UILabel to show the first question (of an array of questions). I'm having some trouble with setting the initial value.
I've done a couple of attempts, whiteout success. I my QuizAppDelegate.h file I declare my UILabel like this:
IBOutlet UILabel * questionField;
In my main .m file I have tried the following:
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
questionField = [[UILabel alloc] init];
[questionField setText:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
Another thing I have tried is the following in QuizAppDelegate:
#property (nonatomic, retain) IBOutlet UILabel *questionField;
- (void)changeTitle:(NSString *)toName;
And in the .m file:
#synthesize questionField;
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
[self changeTitle:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
-(void)changeTitle:(NSString *)toName {
[questionField setText:toName];
}
Any tips on how to solve this would be great!
// Anders
Hopefully you're not actually putting code into main.m. On iOS, you rarely modify that file.
Since you're doing everything in the AppDelegate, let's keep it there (as opposed to creating a new UIViewController). Let's start with the basics.
Adding the Label as an instance variable
You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;
Then, declare the corresponding property, and make sure to synthesize it in the .m file.
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#synthesize questionField // in the .m file
Adding the UILabel in Interface Builder
Open up MainWindow.xib. Drag a UILabel from the Library to the Window that represents your app's window. Then Control-Drag from the AppDelegate object (the third icon on the left in Xcode 4; it'll be labelled in the Document window in IB 3). You'll see a little black window come up—select the option called questionField to make the connection.
See this link for screenshots and how to make connections in IB. The same applies in Xcode 4.
Changing the text
You don't need a separate method to change the text—just modify the label's text property.
Pick a method that'll be called when the app launches (applicationDidFinishLaunching:WithOptions: is a good place to do it in), and put the following code:
questionField.text = #"Hello";
And that's it!
Code
QuizAppDelegate.h
#import <UIKit/UIKit.h>
#interface QuizAppDelegate : NSObject <UIApplicationDelegate> {
IBOutlet UILabel *questionField;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#end
QuizAppDelegate.m
#import "QuizAppDelegate.h"
#implementation QuizAppDelegate
#synthesize window=_window;
#synthesize questionField;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
[self.window addSubview:self.questionField];
[self.window makeKeyAndVisible];
self.questionField.text = #"Hello";
return YES;
}
- (void)dealloc
{
[_window release];
[questionField release];
[super dealloc];
}
#end
If you're creating the label programmatically, then you have to add the label to the view:
[self.view addSubview:questionField];
This assumes that you have a ViewController. If not, and you're doing this directly in the AppDelegate (a very bad idea, by the way), then do
[self.window addSubview:questionField];
If you're creating it in the IB, make sure you set up the connections.
You should not both add the UILabel in the IB and instantiate it programmatically. Only call alloc if you are creating it programmatically. Otherwise, if using the IB, skip that part. You created it already with the xib.
I suspect that you have either not created your Interface Builder layout properly - either you have missed the control out all together or more likely you have not connected that control to the questionField outlet in yout header file.
You need to drag a UILabel view into the main view and then connect it to the correct line in your header file.
You shouldn't be using your main.m like that at all. In fact, you should almost certainly never do anything with it. Try creating a UIViewController subclass and practicing your quiz with that. (Add the UILabel to the IB file and then connect the outlet.) Perhaps use the View-Based Application template while you are practicing.
This is a good answer:
"You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;"
I was trying to change the value of mylabel.text and the screen didn't update the label with this.value. I included the {IBOutlet UILabel * mylabel} and it works like a charm!
So this answer is valid to change the text of a label programmatically!
Thanks