Here is a simple tutorial by a list member of ours:

http://juliuspaintings.co.uk/cgi-bin/paint_css/animatedPaint/059-NSStepper-NSTextField.pl

This example uses an object controller but can be reconfigured to bypass it and 
bind to an ivar easily.

Even though it uses a number formatter besides the controller it exposes the 
problem you are facing, too:

(1) Validation happens only when you leave the field.
(2) The user is not prevented from entering crap values right on the spot.

I took it as a base for some experiments of my own:

By setting validates immediately in IB for the text field and putting a handler 
like

- (BOOL)validateFloatValue:(id *)ioValue error:(NSError **)outError
{
   NSLog(@"validating");
   return YES;
}

into the model class, you can prove that whenever you type a number digit into 
the text field you see a validating message in the console, but at the same 
time the user may happily type any other crap without the validation even 
bothering to kick in. Validation furthermore stops if the value exceeds the max 
value defined in the number formatter. I could not find anything about this in 
the KVC docs, so go figure. I hope it's just me doing something terribly wrong 
or Apple's implementation is rather usless for this purpose.
I guess you might have to implement this method as a supllement if your value 
is set programmatically from various other sources.
All in all it seems way to complicated to achieve the desired effect without 
use of the delegate method. I'd be happy to see an example which does not have 
to rely on it, if this is possible at all. Maybe you have to use a custom value 
converter in the bindings or something. Dunno.

Therefore, my own implementation of a text field + stepper combination goes 
like this:

Specs:
- Only integers are allowed. Single 0 inputs, which eventually would lead to 
leading zeros, are forbidden, since the...
- minimum value is hardcoded at 2 anyway.
- The maximum value is calculated in code (by the window controller's delegate).

So I have a window controller sub-subclass for a modal window implemented like 
this (only relevant parts are shown here):

MyWC.h
@interface MyWC : ContextMenuWC {
   IBOutlet NSTextField *unitNumberField;
   IBOutlet NSStepper *unitNumberStepper;
   
   NSInteger unitSize;
   IBOutlet id delegate;
}

@property (nonatomic, assign) NSInteger unitSize;
@property (nonatomic, assign) id delegate;

- (IBAction) okAction:(id)sender;

- (id)initWithDelegate:(id <SplitColumnToRowsDelegate>)theDelegate;
@end

The stepper's value is bound directly to unitSize. IB binding setting is 
validates immediately.
The NSTextField is equally bound to unitSize. IB binding settings are all off.


MyWC.m

#import "RegexKitLite.h"

- (void)controlTextDidChange:(NSNotification *)aNotification
{
   NSText *theFieldEditor = [aNotification.userInfo 
objectForKey:@"NSFieldEditor"];
   NSString *theString = [theFieldEditor string];
   
   //0-9 only, but no single 0 (i.e. 0 only allowed in double or more digit 
numbers)
   BOOL isAcceptableIntegersOnly = ([theString 
isMatchedByRegex:@"^[0-9]{1,2}$"] && ![theString isMatchedByRegex:@"^0{1,2}$"]);
   
   if (!isAcceptableIntegersOnly) {
      //fall back to max value if input is bad
      [theFieldEditor setString:[NSString stringWithFormat:@"%ld", 
(NSInteger)[unitNumberStepper maxValue]]];
      [theFieldEditor setSelectedRange:NSMakeRange(0, [[theFieldEditor string] 
length])];
      self.unitSize = [[theFieldEditor string] integerValue];
      [self.okButton setEnabled:YES];
      return;
   }
   
   //regex matches, so the following is safe, otherwise integerValue just 
extracts digits from any kind of string value
   NSInteger theInteger = [theString integerValue];
   NSInteger theMinValue = (NSInteger)[unitNumberStepper minValue];
   //if 10 shall be allowed as input, the user has to be able to type 1
   if (theInteger < theMinValue) {
      [self.okButton setEnabled:NO];
      return;
   }
   
   if (theInteger > (NSInteger)[unitNumberStepper maxValue]) {
      //fall back to max value if input is too high
      [theFieldEditor setString:[NSString stringWithFormat:@"%ld", 
(NSInteger)[unitNumberStepper maxValue]]];
      [theFieldEditor setSelectedRange:NSMakeRange(0, [[theFieldEditor string] 
length])];
   }
   [self.okButton setEnabled:YES];
   self.unitSize = [[theFieldEditor string] integerValue];
}


Note that I initialize the bound variable to some predefined value (the maximum 
value possible in this case).
Moreover, even though both GUI elements are bound to self.unitSize, I set this 
value "manually" at the end of the delegate method to make things work.

Took me quite a bit of experimentation to get it as I wanted it. I also 
experimented with KVC validation methods for unitSize but these also didn't 
help.

(Furthermore, I experimented with a @property (copy) previousUnitSizeString as 
a backup to restore a previously accepted value if the code rejects the user 
input, but the user experience didn't feel right for the purpose, so I always 
fall back to the max value. Maybe I'll change my mind eventually to simply 
reject bad input.)

Hope this helps.


Am 17.12.2011 um 05:04 schrieb Koen van der Drift:

> 
> On Dec 16, 2011, at 10:40 AM, Mike Abdullah wrote:
> 
>> Your text field is is bound to the model/a controller right? If so, you want 
>> the "updates immediately" binding option.
>> 
>> On 16 Dec 2011, at 12:59, Koen van der Drift wrote:
>> 
>>> On Thu, Dec 15, 2011 at 10:50 AM, Koen van der Drift
>>> <[email protected]> wrote:
>>>> On Thu, Dec 15, 2011 at 10:17 AM, Mike Abdullah
>>>> <[email protected]> wrote:
>>>> 
>>>>> NSStepper is a subclass of NSControl. Hook up its action/target to be 
>>>>> notified when it's adjusted.
>>>> 
>>>> I'll try that, thanks.  Using bindings sometimes makes you forget that
>>>> there is still some cdong needed :)
>>>> 
>>>> - Koen.
>>> 
>>> (with cdong, I meant coding :)
>>> 
>>> Adding an IBAction did the trick indeed. One aditional question, how
>>> do I make the textfield immediately send the updated value to
>>> controlTextDidChange without the need of htting enter of tabbing out
>>> of the field? See eg the Date/Time preference panel.
>>> 
>>> - Koen.
>> 
> 
> 
> It's not working yet. Whenever I type in the NSTextField, the number shows up 
> twice, eg if I type '6', I see '66'. And I get the message below in the 
> debugger console.  
> 
> Is there some (Apple) sample code that shows how to use a 
> NSTextField/NSStepper combination bound to an integer value?
> 
> Thanks,
> 
> - Koen.
> 
> 
> 
> 2011-12-16 22:45:22.286 MyApp[4566:503] -[__NSCFConstantString 
> unsignedLongLongValue]: unrecognized selector sent to instance 0x7fff7847da00
> 2011-12-16 22:45:22.287 MyApp[4566:503] Exception detected while handling key 
> input.
> 2011-12-16 22:45:22.289 MyApp[4566:503] -[__NSCFConstantString 
> unsignedLongLongValue]: unrecognized selector sent to instance 0x7fff7847da00
> 2011-12-16 22:45:22.297 MyApp[4566:503] (
>       0   CoreFoundation                      0x00007fff8b121286 
> __exceptionPreprocess + 198
>       1   libobjc.A.dylib                     0x00007fff89b53d5e 
> objc_exception_throw + 43
>       2   CoreFoundation                      0x00007fff8b1ad4ce -[NSObject 
> doesNotRecognizeSelector:] + 190
>       3   CoreFoundation                      0x00007fff8b10e133 
> ___forwarding___ + 371
>       4   CoreFoundation                      0x00007fff8b10df48 
> _CF_forwarding_prep_0 + 232
>       5   Foundation                          0x00007fff939f4e7c 
> _NSSetUnsignedLongLongValueForKeyWithMethod + 56
>       6   Foundation                          0x00007fff939a3ded 
> _NSSetUsingKeyValueSetter + 177
>       7   Foundation                          0x00007fff939a38ad 
> -[NSObject(NSKeyValueCoding) setValue:forKey:] + 400
>       8   Foundation                          0x00007fff939d5bb2 
> -[NSObject(NSKeyValueCoding) setValue:forKeyPath:] + 349
>       9   AppKit                              0x00007fff8bb6b33b -[NSBinder 
> _setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error:]
>  + 243
>       10  AppKit                              0x00007fff8bb6aeaa -[NSBinder 
> setValue:forBinding:error:] + 260
>       11  AppKit                              0x00007fff8bf08ecb 
> -[NSValueBinder 
> _applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:]
>  + 191
>       12  AppKit                              0x00007fff8bf08b6f 
> -[NSValueBinder 
> applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:error:]
>  + 591
>       13  AppKit                              0x00007fff8bf08902 
> -[NSValueBinder 
> _applyDisplayedValueIfHasUncommittedChangesWithHandleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:error:]
>  + 154
>       14  AppKit                              0x00007fff8bf07f78 
> -[NSValueBinder 
> validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:] + 
> 488
>       15  AppKit                              0x00007fff8bf4837d 
> -[_NSBindingAdaptor 
> _validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:bindingAdaptor:]
>  + 183
>       16  AppKit                              0x00007fff8bf48488 
> -[_NSBindingAdaptor 
> validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:] + 
> 256
>       17  AppKit                              0x00007fff8be62cc5 
> -[NSTextField textDidChange:] + 187
>       18  Foundation                          0x00007fff9397cde2 
> __-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_1 + 
> 47
>       19  CoreFoundation                      0x00007fff8b0c9e0a 
> _CFXNotificationPost + 2634
>       20  Foundation                          0x00007fff93969097 
> -[NSNotificationCenter postNotificationName:object:userInfo:] + 65
>       21  AppKit                              0x00007fff8bec4130 
> -[NSTextView(NSSharing) didChangeText] + 348
>       22  AppKit                              0x00007fff8bebe778 
> _NSDoUserReplaceForCharRange + 484
>       23  AppKit                              0x00007fff8bebe7e3 
> _NSDoUserDeleteForCharRange + 40
>       24  AppKit                              0x00007fff8beabd64 
> -[NSTextView(NSKeyBindingCommands) deleteBackward:] + 441
>       25  CoreFoundation                      0x00007fff8b110a1d -[NSObject 
> performSelector:withObject:] + 61
>       26  AppKit                              0x00007fff8bdb8bad 
> -[NSResponder doCommandBySelector:] + 62
>       27  AppKit                              0x00007fff8be9390e -[NSTextView 
> doCommandBySelector:] + 198
>       28  AppKit                              0x00007fff8bcecfff 
> -[NSKeyBindingManager(NSKeyBindingManager_MultiClients) 
> interpretEventAsCommand:forClient:] + 1799
>       29  AppKit                              0x00007fff8c03eb4a 
> -[NSTextInputContext handleEvent:] + 747
>       30  AppKit                              0x00007fff8bf0aeaf -[NSView 
> interpretKeyEvents:] + 248
>       31  AppKit                              0x00007fff8be83c65 -[NSTextView 
> keyDown:] + 691
>       32  AppKit                              0x00007fff8b963544 -[NSWindow 
> sendEvent:] + 7430
>       33  AppKit                              0x00007fff8b8fb68f 
> -[NSApplication sendEvent:] + 5593
>       34  AppKit                              0x00007fff8b891682 
> -[NSApplication run] + 555
>       35  AppKit                              0x00007fff8bb1080c 
> NSApplicationMain + 867
>       36  Spectrum                            0x000000010882a1c2 main + 34
>       37  Spectrum                            0x000000010882a194 start + 52
>       38  ???                                 0x0000000000000003 0x0 + 3
> )
> 
> 
> 
> 
> 
> 
> _______________________________________________
> 
> Cocoa-dev mailing list ([email protected])
> 
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
> 
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/magnard%40web.de
> 
> This email sent to [email protected]
> 

_______________________________________________

Cocoa-dev mailing list ([email protected])

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [email protected]

Reply via email to