Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Cocoa 将NSStepper与NSTextField集成_Cocoa_User Interface - Fatal编程技术网

Cocoa 将NSStepper与NSTextField集成

Cocoa 将NSStepper与NSTextField集成,cocoa,user-interface,Cocoa,User Interface,我需要一个NSTextField作为一个控件与NSStepper一起工作,这样我就可以通过直接在文本字段上更改整数值或使用stepper向上/向下箭头来编辑整数值 在IB中,我添加了这两个控件,然后将NSStepper的takeIntValueFrom连接到NSTextField,这样每当我单击stepper箭头时,文本值都会发生变化。问题是,如果我编辑文本字段,然后再次单击步进器,它将忘记我手动编辑的值,并使用步进器的内部值 当文本字段值更改时,更新步进器值的最佳/最简单方法是什么 跳过tak

我需要一个NSTextField作为一个控件与NSStepper一起工作,这样我就可以通过直接在文本字段上更改整数值或使用stepper向上/向下箭头来编辑整数值

在IB中,我添加了这两个控件,然后将NSStepper的takeIntValueFrom连接到NSTextField,这样每当我单击stepper箭头时,文本值都会发生变化。问题是,如果我编辑文本字段,然后再次单击步进器,它将忘记我手动编辑的值,并使用步进器的内部值


当文本字段值更改时,更新步进器值的最佳/最简单方法是什么

跳过
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:);
}