Это для людей, которым небезразлично какао.
![NSSteppers in action](https://i.stack.imgur.com/eimxq.png)
Единственная причина использования NSStepper
вместе с NSTextField
состоит в том, что в текстовом поле есть какое-то число.
Шаги для полного расширенного решения Какао (которого, к сожалению, здесь нет):
Шаг 1: добавьте средства форматирования чисел к вашим текстовым полям и отформатируйте их по своему желанию.
![Number formatter](https://i.stack.imgur.com/BbMCT.png)
Шаг 2: добавьте NSObjectController
и приклейте к нему свои текстовые поля / степперы. Это распространенная ошибка, когда люди делают прямые привязки. Мех. Добавьте соответствующие keyPaths, которые есть в вашей модели.
![Bindings](https://i.stack.imgur.com/11vrT.png)
Шаг 3: убедитесь, что ваши текстовые поля реагируют на ключевые события. Всегда скучаю по новичкам. Подключите делегат текстового поля к нашему контроллеру и добавьте код.
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector
{
if (commandSelector == @selector(moveUp:) || commandSelector == @selector(moveDown:)) {
if (control == [self minAgeTextField]) {
return [[self minAgeStepper] sendAction:commandSelector to:[self minAgeStepper]];
}
if (control == [self maxAgeTextField]) {
return [[self maxAgeStepper] sendAction:commandSelector to:[self maxAgeStepper]];
}
}
return NO;
}
Шаг 4: Немного кода клея. Это также место, где мы устанавливаем содержимое нашего objectController.
@property (weak) IBOutlet NSObjectController *profilesFilterObjectController;
@property (weak) IBOutlet NSTextField *minAgeTextField;
@property (weak) IBOutlet NSTextField *maxAgeTextField;
@property (weak) IBOutlet NSStepper *minAgeStepper;
@property (weak) IBOutlet NSStepper *maxAgeStepper;
@property (nonatomic) ProfilesFilter *filter;
- (void)awakeFromNib //or viewDidLoad...
{
[self setFilter:[ProfilesFilter new]];
[[self profilesFilterObjectController] setContent:[self filter]];
}
Шаг 5. Подтвердите свои значения (проверка KVC)
@implementation ProfilesFilter
- (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable __autoreleasing *)outError
{
if ([inKey isEqualToString:@"minAge"] || [inKey isEqualToString:@"maxAge"]) {
if (*ioValue == nil) {
return YES;
}
NSInteger minAge = [[self minAge] integerValue];
NSInteger maxAge = [[self maxAge] integerValue];
if ([inKey isEqualToString:@"minAge"]) {
if (maxAge != 0) {
*ioValue = @(MAX(18, MIN([*ioValue integerValue], maxAge)));
}
}
if ([inKey isEqualToString:@"maxAge"]) {
if (minAge != 0) {
*ioValue = @(MIN(99, MAX([*ioValue integerValue], minAge)));
}
}
}
return YES;
}
@end
Примечания: неверные значения? NSNumberFormatter покажет ошибку. Максимальный возраст ниже минимального возраста? Мы используем проверку KVC (шаг 5). Эврика!
БОНУС: Что делать, если пользователь удерживает клавишу CTRL или SHIFT или оба (пользователь хочет более медленное или более быстрое увеличение)? Мы можем изменить приращение на основе нажатой клавиши (подкласс NSStepper
и переопределение increment
геттер и проверить, например, NSEvent.modifierFlags.contains(.shift))
.
- (double)increment
{
BOOL isShiftDown = ([NSEvent modifierFlags] & NSEventModifierFlagShift) ? YES : NO;
//BOOL isOptionDown = ([NSEvent modifierFlags] & NSEventModifierFlagOption) ? YES : NO;
double increment = ([self defaultIncrement] - 0.001 > 0) ? [self defaultIncrement] : 1.0;
if (isShiftDown) {
increment = increment * 5;
}
return increment;
}
Добавить к - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector
//JUST AN ILLUSTRATION, holding shift + key up doesn't send moveUp but moveUpAndModifySelection (be careful of crash, just modify the command to moveUp; if not `NSStepper` doesn't know `moveUpAndModifySelection`)
if (commandSelector == @selector(moveUpAndModifySelection:)) {
commandSelector = @selector(moveUp:);
}
if (commandSelector == @selector(moveToEndOfDocument:) || commandSelector == @selector(moveDownAndModifySelection:)) {
commandSelector = @selector(moveDown:);
}
PS: Лучшее решение - использовать пользовательский NSTextField
, который имеет и рисует степпер и контролирует все события. В итоге вы получаете меньший контроллер!