shouldChangeCharactersInRange affecting all UITextField's - uitextfield

I have two UITextField in my app, one is for price and the other is a label for a product.
I have defined both UITextField's with #property and #synthesize into the .m
(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
I'm using this method to limit the entry for the price field but it seems to be affecting both field. How to limit it to one field?

The method is called when any of the UITextFields have this instance set as a delegate in Interface Builder or with code.
You can check which field calls it with something like:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField == yourSynthesizedPropertyForPriceField) {
//DO SOMETHING
}
return YES;
}

Related

Crash in objc_retain in method performed with performSelector

I have this strange crash relating to ARC auto-inserting objc_retains in my code.
I have the following two classes:
#interface MenuItem : NSObject
#property (weak, nonatomic) id target;
#property (unsafe_unretained, nonatomic) SEL action;
#property (strong, nonatomic) id object;
- (instancetype)initWIthTarget:(id)target action:(SEL)action withObject:(id)object;
- (void)performAction;
#end
#implementation MenuItem
- (void)performAction
{
if (self.target && self.action)
{
if (self.object)
{
[self.target performSelector:self.action withObject:self.object];
}
else
{
[self.target performSelector:self.action];
}
}
}
#end
#interface Widget : NSObject
- (void)someMethod:(id)sender;
#end
At some point I instantiate a MenuItem as such:
MenuItem *item = [MenuItem alloc] initWithTarget:widget action:#selector(someMethod:) object:nil];
Then elsewhere I invoke performAction on the menu item:
[item performAction];
In the implementation of someMethod I get a crash:
#implementation Widget
- (void)someMethod:(id)sender
{
// EXEC_BAD_ACCESS crash in objc_retain
}
#end
Why is this happening?
The reason for the crash was because I was using the wrong performSelector.
NSObject defines multiple versions of performSelector. The one I was invoking was:
- (id)performSelector:(SEL)aSelector;
However the method I was invoking took an id parameter. Eg:
- (void)someMethod:(id)sender;
Now ARC being the nice safe memory management system that it is tries to ensure that parameters are properly retained during the execution of a method. So even though my someMethod: was empty ARC was producing code that looked like this:
- (void)someMethod:(id)sender
{
objc_retain(sender);
objc_release(sender);
}
The problem with this however was that I was invoking performSelector: and not supplying a value for the sender parameter. So sender was pointing at random junk on the stack. Therefore when objc_retain() was invoked the app crashed.
If I change:
MenuItem *item = [[MenuItem alloc] initWithTarget:widget
action:#selector(someMethod:)
object:nil];
to
MenuItem *item = [[MenuItem alloc] initWithTarget:widget
action:#selector(someMethod)
object:nil];
and
- (void)someMethod:(id)sender;
to
- (void)someMethod;
Then the crash goes away.
Similarly I can also change
[self.target performSelector:self.action];
to
[self.target performSelector:self.action withObject:nil];
if I want to follow the 'standard' form of target-action methods that take a single parameter. The benefit of the second form of performSelector is that if I'm invoking a method that doesn't take a parameter it will still work fine.

Get keyDown event for an NSTextField

In xcode last version, I am trying to get the keyDown event of an NSTextField.
However, despite following multiple tutorials on the internet (delegates, controllers...), I still can't receive it.
Any easy hint for me ?
Thanks !
I got sick of all the non answers to do it some other way people so I put my nose down and figured out a way to make this work. This isn't using keydown event directly but it is using the keydown in the block. And the behavior is exactly what I wanted.
Subclass the text field
.h
#interface LQRestrictedInputTextField : NSTextField
.m
In the become first responder setup a local event
static id eventMonitor = nil;
- (BOOL)becomeFirstResponder {
BOOL okToChange = [super becomeFirstResponder];
if (okToChange) {
[self setKeyboardFocusRingNeedsDisplayInRect: [self bounds]];
if (!eventMonitor) {
eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event) {
NSString *characters = [event characters];
unichar character = [characters characterAtIndex:0];
NSString *characterString=[NSString stringWithFormat:#"%c",character];
NSArray *validNonAlphaNumericArray = #[#" ",#"(",#")",#"[",#"]",#":",#";",#"\'",#"\"",#".",#"<",#">",#",",#"{",#"}",#"|",#"=",#"+",#"-",#"_",#"?",#"#",
#(NSDownArrowFunctionKey),#(NSUpArrowFunctionKey),#(NSLeftArrowFunctionKey),#(NSRightArrowFunctionKey)];
if([[NSCharacterSet alphanumericCharacterSet] characterIsMember:character] || character == NSCarriageReturnCharacter || character == NSTabCharacter || character == NSDeleteCharacter || [validNonAlphaNumericArray containsObject:characterString ] ) { //[NSCharacterSet alphanumericCharacterSet]
} else {
NSBeep();
event=nil;
}
return event;
} ];
}
}
NSLog(#"become first responder");
return okToChange;
}
remove the event once the textfield editing ends
Also if you're using ARC I noticed you might need to assign the textview string to the stringValue. I nslog'd the stringValue and the value was retained. Without the nslog I had to assign the notification object string to the stringValue to keep it from getting released.
-(void) textDidEndEditing:(NSNotification *)notification {
[NSEvent removeMonitor:eventMonitor];
eventMonitor = nil;
NSTextView *textView=[notification object];
self.stringValue=textView.string;
}
You can subclass NStextField and use keyUp that works for NSTextField.
in .h
#interface MyTextField : NSTextField <NSTextFieldDelegate>
in .m
-(void)keyUp:(NSEvent *)theEvent {
NSLog(#"Pressed key in NStextField!");
}
Add UITextFieldDelegate to your .h like this
#interface ViewController : UIViewController <UITextFieldDelegate> {
Then you can use this to detect every key press in a text field
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Return YES to allow the character that was pressed to be inserted into the field but you can add whatever code you need in here.

How I tell my UITextField (code) to resign first responder?

I have created my UITextField by code, without InterfaceBuilder. I want the keyboard to disappear when the button "Done" is pushed. How does the code know that I am referending to an UITextField and no to other one
First, thanks a lot.
My code is like this:
-(void)viewDidLoad {
[super viewDidLoad];
field = [[UITextField alloc] initWithFrame:CGRectMake(30, 100, 185, 30)];
field.adjustsFontSizeToFitWidth = YES;
field.textColor = [UIColor blackColor];
field.placeholder = #"Text";
field.keyboardType = UIKeyboardTypeDefault;
field.returnKeyType = UIReturnKeyDone;
field.backgroundColor = [UIColor blueColor];
field.delegate = self;
[self.view addSubview:field];
}
......
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
}
With this code I push the button Done and nothing happen. Is like that how you say?
Edit:
I've created two UITextField how I did with the previous one. But now, for each row I do this:
if ([indexPath row] == 0) {
[cell.contentView addSubview:pTextField];
}
else {
[cell.contentView addSubview:pTextField];
}
So with this code, the program received signal "EXC_BAD_ACCESS". Any idea why happen this?
How does the code know that I am referending to an UITextField and no to other one
Your textFieldShouldReturn: method's textField parameter will always be the text field that is currently active.
The method has to return a BOOL, you should be getting compiler warnings with it as it stands. Try
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
Note that you are also currently leaking memory in the way you add the text field. You should set it as a property as per WrightCS's answer so that you can refer to it later on. So at the end of your viewDidLoad:
self.myTextField = field;
[field release];
Define your textField in your header, then you can use the following:
.h
#interface MyeViewController : UIViewController <UITextFieldDelegate>
{
UITextField * myTextField;
}
#end
.m
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[myTextField resignFirstResponder];
/* textField here is referenced from
textFieldShouldReturn:(UITextField *)textField
*/
}
Make sure you set the delegate of your programatically created UITextField to self (the view controller that created the object) and implement the appropriate UITextFieldDelegate method (I think its textFieldShouldReturn:) and call in that method resignFirstResponder on the textField argument passed to the delegate method (which will be your UITextField).

Getting the text from a NSTextField and setting it to an already defined String

Im brand new to Cocoa, and im struggling to find the answer and figure it out.
if I have a NSTextField how can I get the value (stringvalue) of that and save it in a predefined string variable, there after display it in the debug using NSLog()?
Thanks
Just use the stringValue method by NSTextField and save it to a NSString
#interface MyClass : NSObject
{
NSTextField *textField;
}
-(IBAction)displayString:(id)sender;
#end
#implementation
-(IBAction)displayString:(id)sender
{
NSString *string = [textField stringValue];
NSLog (#"%#", string);
}
#end
Just connect the displayString method to the NSTextField and it should work.

NSTextView value changed

I'm pretty new to mac development (coming from a web and iOS background) and I can't work out how I could get a notification every time the value of an NSTextView changes. Any ideas?
Ups I just saw that you want a callback from NSTextView and not NSTextField
Just add in the header of the object which should be the delegate the protocol
#interface delegateAppDelegate : NSObject <NSApplicationDelegate, NSTextViewDelegate> {
NSWindow *window;
}
After that you add a method like
-(void)textDidChange:(NSNotification *)notification {
NSLog(#"Ok");
}
Make sure you connected the delegate property of the NSTextView (not NSScrollView) with the object which should receive the delegate
Here's the solution:
NSTextView *textView = ...;
#interface MyClass : NSObject<NSTextStorageDelegate>
#property NSTextView *textView;
#end
MyClass *myClass = [[MyClass alloc] init];
myClass.textView = textView;
textView.textStorage.delegate = myClass;
#implementation MyClass
- (void)textStorageDidProcessEditing:(NSNotification *)aNotification
{
// self.textView.string will be the current value of the NSTextView
// and this will get invoked whenever the textView's value changes,
// BOTH from user changes (like typing) or programmatic changes,
// like textView.string = #"Foo";
}
#end
set the nstextfield's delegate. in the .h file of the delegate you add the delegate protocol
In the .m file you add a method like -(void)controlTextDidChange:(NSNotification *)obj {
NSLog(#"ok");
}
I hope that helps
Set the delegate and then use
- (void) controlTextDidChange: (NSNotification *) notification
{
}

Resources