我需要让NSTextField使用NSStepper作为一个控件,以便我可以通过直接在文本字段上更改它或使用步进向上/向下箭头来编辑整数值。
在IB中我添加了这两个控件,然后将NSStepper的takeIntValueFrom连接到NSTextField,这使得每次单击步进箭头时文本值都会改变。问题是,如果我编辑文本字段然后再次单击步进器,它将忘记我手动编辑的值并使用步进器的内部值。
每当更改文本字段值时,更新步进器值的最佳/最简单方法是什么?
答案 0 :(得分:23)
跳过takeIntValueFrom:
方法。而是将两个视图绑定到控制器中的同一属性。您可能还想创建一个格式化程序并将文本字段的formatter
出口连接到它。
答案 1 :(得分:15)
我会有一个带有一个整数变量的模型,它代表两个控件的值。
在我的控制器中,我将使用一个连接到两个控件的IBAction和两个IBOutlet,每个控件一个。那么我会有一种从模型值更新出口的方法。
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]];
}
这是原则。正如Peter Hosey所述,格式化程序可能对您的文本字段很有用,至少要考虑步进器的最小值和最大值。
答案 2 :(得分:6)
我发现简单的方法是将步进器值绑定到输入和输入值到步进器
@property (strong) IBOutlet NSTextField *timeInput;
@property (strong) IBOutlet NSStepper *timeStepper;
答案 3 :(得分:5)
如果要跟踪某个模型中某个字段的值,例如当前页码,则无需在步进控件中保留另一个副本。我只是将控件配置为初始值为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 :(得分:4)
我按Peter Hosey建议,因为在我看来这是最干净的方法。在我的控制器中创建了一个属性:
int editValue_;
...
@property (nonatomic, readwrite) int editValue;
...
@synthesize editValue = editValue_;
然后在IB中为Bindings选项卡中的两个控件设置了“Bind to:”复选框并选中了我的控制器,然后在“Model Key Path”字段中设置了“editValue”和voilá,它有效!只需3行代码和一些IB编辑。如果我需要更改控制器上的值,我使用setEditValue:文本字段会更新。
答案 5 :(得分:0)
这是给那些关心可可的人的。
与NSStepper
一起使用NSTextField
的唯一原因是因为文本字段中有一些数字。
完整的高级可可解决方案的步骤(可悲的是,这里缺少):
第1步:在您的文本字段中添加数字格式器,并根据需要设置格式。
第2步:添加NSObjectController
并将您的文本字段/步进粘贴到其中。当人们进行直接绑定时,这是一个常见的错误。嗯在模型中添加相应的keyPath。
第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
,它具有并绘制步进并控制所有事件。您最终会得到一个较小的控制器!