Cocoa 将NSStepper与NSTextField集成
我需要一个NSTextField作为一个控件与NSStepper一起工作,这样我就可以通过直接在文本字段上更改整数值或使用stepper向上/向下箭头来编辑整数值 在IB中,我添加了这两个控件,然后将NSStepper的takeIntValueFrom连接到NSTextField,这样每当我单击stepper箭头时,文本值都会发生变化。问题是,如果我编辑文本字段,然后再次单击步进器,它将忘记我手动编辑的值,并使用步进器的内部值Cocoa 将NSStepper与NSTextField集成,cocoa,user-interface,Cocoa,User Interface,我需要一个NSTextField作为一个控件与NSStepper一起工作,这样我就可以通过直接在文本字段上更改整数值或使用stepper向上/向下箭头来编辑整数值 在IB中,我添加了这两个控件,然后将NSStepper的takeIntValueFrom连接到NSTextField,这样每当我单击stepper箭头时,文本值都会发生变化。问题是,如果我编辑文本字段,然后再次单击步进器,它将忘记我手动编辑的值,并使用步进器的内部值 当文本字段值更改时,更新步进器值的最佳/最简单方法是什么 跳过tak
当文本字段值更改时,更新步进器值的最佳/最简单方法是什么 跳过
takeIntValueFrom:
方法。相反,将两个视图绑定到控制器中的同一属性。您可能还希望创建一个格式化程序,并将文本字段的格式化程序连接到它。我将有一个带有一个整数变量的模型,它表示两个控件的值
在我的控制器中,我将使用一个iAction连接到两个控件,以及两个iBouts,每个控件一个。然后,我将有一个从模型值更新插座的方法
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]];
}
这是原则。正如所说,格式化程序可能对您的文本字段很有用,至少要考虑步进器的最小值和最大值。我按照建议做了,因为在我看来这是最干净的方法。在我的控制器中创建了一个属性:
int editValue_;
...
@property (nonatomic, readwrite) int editValue;
...
@synthesize editValue = editValue_;
然后在IB中,对于绑定选项卡中的两个控件,我设置了“绑定到:”复选框并选择了我的控制器,然后在“Model Key Path”字段中设置了“editValue”,瞧,成功了!只需3行代码和一些IB编辑。如果我需要更改控制器上的值,我会使用setEditValue:并更新文本字段。我发现简单的方法是将步进器值绑定到输入,将输入值绑定到步进器
@property (strong) IBOutlet NSTextField *timeInput;
@property (strong) IBOutlet NSStepper *timeStepper;
如果要跟踪模型中某个字段的值,例如当前页码,则无需在步进控制中保留另一个副本。我只是将控件配置为初始值为0,范围为-1到1。在步进控制的iAction方法中,如果在控制上单击(或自动重复),会调用该方法,请询问其当前值,如果单击了向上箭头,则当前值为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];
}
这样,步进控制根本不必与模型中的任何值链接。这是为关心可可的人准备的
将NSStepper
与NSTextField
一起使用的唯一原因是文本字段中有一些数字
完整的高级可可粉解决方案的步骤(此处遗憾地缺失):
步骤1:将数字格式化程序添加到文本字段中,并根据需要进行格式化
步骤2:添加NSObjectController
,并将文本字段/步进器粘贴到其中。当人们进行直接绑定时,这是一个常见的错误。无聊的。在模型中添加相应的关键路径
步骤3:确保您的文本字段对关键事件做出反应。总是被新手错过。将textfield委托挂接到控制器并添加代码
- (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验证)
注意:错误的值?NSNumberFormatter将显示错误。最大年龄低于最小年龄?我们使用KVC验证(步骤5)。尤里卡
额外好处:如果用户按住CTRL键或SHIFT键,或者同时按住CTRL键或SHIFT键(用户希望缓慢或快速的增量),该怎么办?我们可以根据按下的键修改增量(子类NSStepper
和重写器increment
getter,并检查例如NSEvent.modifierFlags.contains(.shift))
将此添加到-(BOOL)控件:(NSControl*)控件文本视图:(NSTextView*)文本视图doCommandBySelector:(SEL)命令选择器
//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
,它具有并绘制步进器并控制所有事件。你最终会得到一个更小的控制器 对于最新版本的Xcode,这只是@属性
行,无需使用@合成
或制作实际的ivar。诚然,我在将近4年后添加了这条评论,@synthesis
行在提问/回答问题时是必要的。+1因为它澄清了一点任务。彼得·霍西(Peter Hosey)的精彩提示,顺便说一句。在某些情况下可能还可以,但它跳过了在MVC中没有“C”的控制器。如果您不想将两个控件绑定到一个公共属性,而只是绑定到彼此,这是正确的方法。它使事情变得非常简单,无需编写太多代码。对我来说就像champ一样。这给了我更多的控制。如此简单,如此聪明,如此优雅的解决方案!谢谢当设置步进器的增量而不是1时也可以工作。它可以工作,但一旦你深入到高级主题,如Nsundomanager,你就会优雅地失败。正确的解决方案是绑定到NSObjectController并分别设置内容对象。不要对抗框架,学习它们!嘿,马瑞克,谢谢你的回答。是否有一种方便的方式来打包所有这些内容(一个NSTextField
及其附带的NSStepper
),以便您可以将它们作为代码中的单个对象来处理?也就是说,让您的代码只查看MySteppedTextField
,并与之交互,就好像它是任何旧的NSTextField
,它旁边碰巧还有一个步进器注意:不需要NSObjectController
,绑定到NSViewController
<代码>NSNumberFormatter
不显示错误,绑定显示错误。我正在学习使用Visual Studio C#for Mac开发macOS,它与XCode Interface Builder一起工作。我在找你
- (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;
}
//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:);
}