Интегрировать NSStepper с NSTextField - PullRequest
14 голосов
/ 01 апреля 2009

Мне нужно, чтобы NSTextField работал с NSStepper как с одним элементом управления, чтобы я мог редактировать целочисленное значение либо путем изменения его непосредственно в текстовом поле, либо с помощью стрелок вверх / вниз.

В IB я добавил оба этих элемента управления, затем подключил takeIntValueFrom от NSStepper к NSTextField, и это заставляет текстовое значение изменяться всякий раз, когда я нажимаю на стрелки степпера. Проблема заключается в том, что если я отредактирую текстовое поле, а затем снова нажму на степпер, он забудет о значении, которое я отредактировал вручную, и будет использовать внутреннее значение степпера.

Каков наилучший / самый простой способ обновления значения степпера при каждом изменении значения текстового поля?

Ответы [ 6 ]

23 голосов
/ 01 апреля 2009

Пропустить метод takeIntValueFrom:. Вместо этого свяжите оба представления с одним и тем же свойством в вашем контроллере. Вы также можете создать форматтер и подключить к нему текстовое поле formatter.

15 голосов
/ 01 апреля 2009

У меня была бы модель с одной целочисленной переменной, которая представляет значение обоих элементов управления.

В моем контроллере я бы использовал один IBAction, подключенный к обоим элементам управления, и два IBOutlets, по одному для каждого элемента управления. тогда у меня был бы метод для обновления торговых точек от значения модели.

IBOutlet NSStepper * stepper;
IBOutlet NSTextField * textField;

- (IBAction) controlDidChange: (id) sender
{
    [model setValue:[sender integerValue]];
    [self updateControls];
}

- (void) updateControls
{
    [stepper setIntegerValue:[model value]];
    [textField setIntegerValue:[model value]];
}

Это принцип. Как сказал Питер Хоси , для вашего текстового поля может быть полезен форматер, по крайней мере, для учета минимального и максимального значений степпера.

6 голосов
/ 07 июня 2014

Я нашел простой способ привязать значение шага к входу, а значение ввода к шагу

@property (strong) IBOutlet NSTextField *timeInput;
@property (strong) IBOutlet NSStepper *timeStepper;

bind input value

bind stepper value

5 голосов
/ 11 апреля 2016

Если кто-то отслеживает значение поля в своей модели, например номер текущей страницы, тогда нет необходимости сохранять другую копию в элементе управления шаговым двигателем. Я просто настраиваю элемент управления, чтобы иметь начальное значение 0 и диапазон от -1 до 1. В методе IBAction для шагового элемента управления, который вызывается для любого щелчка (или для автоматического повтора) элемента управления, запрашивается его текущее значение, которое будет равно 1, если была нажата стрелка вверх, или -1, если стрелка вниз. Немедленно сбросьте текущее значение элемента управления на 0, а затем обновите модель и все остальное (связанное текстовое поле или новый просмотр страницы и т. Д.) Новым значением, основанным на направлении 1 или -1. Например.,

- (IBAction) bumpPageNum:(id)sender
{
    int whichWay = [sender intValue];   // Either 1 or -1
    [sender setIntValue:0];             // Same behavior next time
    [model changePageBy:whichWay];
}

Таким образом, шаговый регулятор вообще не нужно связывать с какими-либо значениями в модели.

4 голосов
/ 01 апреля 2009

Я сделал так, как Питер Хоси предложил, так как мне кажется, самый чистый подход. Создано свойство в моем контроллере:

int editValue_;
...
@property (nonatomic, readwrite) int editValue;
...
@synthesize editValue = editValue_;

затем в IB для обоих элементов управления на вкладке «Привязки» я установил флажок «Привязать к:» и выбрал мой контроллер, затем в поле «Путь к ключу модели» установил «editValue» и вуаля, все заработало! Всего 3 строки кода и немного редактирования IB. И если мне нужно изменить значение на моем контроллере, я использую setEditValue: текстовое поле обновляется.

0 голосов
/ 26 февраля 2019

Это для людей, которым небезразлично какао.

NSSteppers in action

Единственная причина использования NSStepper вместе с NSTextField состоит в том, что в текстовом поле есть какое-то число.

Шаги для полного расширенного решения Какао (которого, к сожалению, здесь нет):

Шаг 1: добавьте средства форматирования чисел к вашим текстовым полям и отформатируйте их по своему желанию.

Number formatter

Шаг 2: добавьте NSObjectController и приклейте к нему свои текстовые поля / степперы. Это распространенная ошибка, когда люди делают прямые привязки. Мех. Добавьте соответствующие keyPaths, которые есть в вашей модели.

Bindings

Шаг 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, который имеет и рисует степпер и контролирует все события. В итоге вы получаете меньший контроллер!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...