NSButtonCell内部定制NSCell

Tob*_*obi 9 cocoa tableview nscell nsbuttoncell

在我的cocoa应用程序中,我需要一个用于NSTableView的自定义NSCell.此NSCell子类包含用于处理单击的自定义NSButtonCell(以及用于文本内容的两个或三个NSTextFieldCells).您将在下面找到我的代码的简化示例.

@implementation TheCustomCell

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
   // various NSTextFieldCells
   NSTextFieldCell *titleCell = [[NSTextFieldCell alloc] init];
   ....
   // my custom NSButtonCell
   MyButtonCell *warningCell = [[MyButtonCell alloc] init];
   [warningCell setTarget:self];
   [warningCell setAction:@selector(testButton:)];
   [warningCell drawWithFrame:buttonRect inView:controlView];
}
Run Code Online (Sandbox Code Playgroud)

我坚持的问题是:在这个NSCell中使用该按钮(更确切地说:NSButtonCell)以正常工作的最佳/正确方法什么?"工作"表示:触发指定的操作消息并在单击时显示备用图像.开箱即用,按钮在单击时不执行任何操作.

很难找到关于这个主题的信息/读物.我在网上找到的唯一帖子指出我要实施

- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp; 
Run Code Online (Sandbox Code Playgroud)

这是正确的方法吗?实现trackMouse:在我的包含NSCell中?然后将事件转发给NSButtonCell?我本来期望NSButtonCell本身知道在点击它时该做什么(我看到trackMouse:方法更多的是在cunjunction,真正跟踪鼠标移动 - 而不是作为'标准'点击行为的训练轮).但是当它被包含在一个单元格本身时似乎不会这样做......似乎我还没有掌握自定义单元格的大图,但是;-)

如果有人能够根据自己的经验回答这个问题(或指向我的某些教程等),我会很高兴 - 告诉我我是否走在正确的轨道上.

托比,提前谢谢

Jer*_*man 8

最低要求是:

  • 将鼠标放在按钮上后,只要鼠标悬停在按钮上,就必须按下鼠标.
  • 如果鼠标然后释放按钮,则您的单元格必须发送相应的操作消息.

要按下按钮,您需要根据需要更新按钮单元的highlighted属性.单独更改状态不会实现此目的,但您想要的是,当且仅当其状态为按钮时,才会突出显示该按钮NSOnState.

要发送操作消息,您需要知道何时释放鼠标,然后使用-[NSApplication sendAction:to:from:]发送消息.

为了能够发送这些消息,您需要使用提供的事件跟踪方法NSCell.请注意,除最终方法外,所有这些跟踪方法都-stopTracking:...返回一个布尔值来回答问题"您是否要继续接收跟踪消息?"

最后的转折是,为了发送任何跟踪消息,您需要实现-hitTestForEvent:inRect:ofView:并返回适当的NSCellHit...值掩码.具体来说,如果返回的值没有NSCellHitTrackableArea值,则不会获得任何跟踪消息!

因此,在较高的层次上,您的实现将类似于:

- (NSUInteger)hitTestForEvent:(NSEvent *)event
                       inRect:(NSRect)cellFrame
                       ofView:(NSView *)controlView {
    NSUInteger hitType = [super hitTestForEvent:event inRect:cellFrame ofView:controlView];

    NSPoint location = [event locationInWindow];
    location = [controlView convertPointFromBase:location];
    // get the button cell's |buttonRect|, then
    if (NSMouseInRect(location, buttonRect, [controlView isFlipped])) {
        // We are only sent tracking messages for trackable areas.
        hitType |= NSCellHitTrackableArea;
    }
    return hitType;
}

+ (BOOL)prefersTrackingUntilMouseUp {
   // you want a single, long tracking "session" from mouse down till up
   return YES;
}

- (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView {
   // use NSMouseInRect and [controlView isFlipped] to test whether |startPoint| is on the button
   // if so, highlight the button
   return YES;  // keep tracking
}

- (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView {
   // if |currentPoint| is in the button, highlight it
   // otherwise, unhighlight it
   return YES;  // keep on tracking
}

- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag {
   // if |flag| and mouse in button's rect, then
   [[NSApplication sharedApplication] sendAction:self.action to:self.target from:controlView];
   // and, finally,
   [buttonCell setHighlighted:NO];
}
Run Code Online (Sandbox Code Playgroud)


Hup*_*tes 5

的点NSCell的子类是分离用于再现和处理来自的visual-和事件层次的责任公共UI元素(对照)责任NSView类.这种配对允许每个配对提供更大的专业性和可变性而不会给另一个带来负担.看看NSButton可以在Cocoa中创建的大量实例.想象一下,NSButton如果缺少功能上的这种分割,那么将存在的子类数量!

使用设计模式语言来描述角色:NSControl充当外观,从其客户端隐藏其组成的细节,并将事件和呈现消息呈现给NSCell充当委托的实例.

因为您的NSCellNSCell类在其组合中包含其他子类实例,所以它们不再直接从NSControl视图层次结构中的实例接收这些事件消息.因此,为了使这些单元实例从事件响应器链(视图层次结构)接收事件消息,您的单元实例需要传递这些相关事件.您正在重新创建NSView层次结构的工作.

这不一定是坏事.通过复制NSControl(及其NSView超类)但在NSCell表单中的行为,您可以按位置,事件类型或其他条件过滤传递到子单元格的事件.缺点是复制了NSView/NSControl构建过滤和管理机制的工作.

因此,在设计界面时,您需要考虑NSButtonCell(和NSTextFieldCells)NSControl在普通视图层次结构中的s或者子NSCell类中的子单元格中是否更好.这是更好地利用其已经在一个代码库中存在,你的功能,而不是重新发明它(并在以后继续保持它)不必要的.