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,真正跟踪鼠标移动 - 而不是作为'标准'点击行为的训练轮).但是当它被包含在一个单元格本身时似乎不会这样做......似乎我还没有掌握自定义单元格的大图,但是;-)
如果有人能够根据自己的经验回答这个问题(或指向我的某些教程等),我会很高兴 - 告诉我我是否走在正确的轨道上.
托比,提前谢谢
最低要求是:
要按下按钮,您需要根据需要更新按钮单元的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)
的点NSCell
的子类是分离用于再现和处理来自的visual-和事件层次的责任公共UI元素(对照)责任NSView
类.这种配对允许每个配对提供更大的专业性和可变性而不会给另一个带来负担.看看NSButton
可以在Cocoa中创建的大量实例.想象一下,NSButton
如果缺少功能上的这种分割,那么将存在的子类数量!
使用设计模式语言来描述角色:NSControl
充当外观,从其客户端隐藏其组成的细节,并将事件和呈现消息呈现给NSCell
充当委托的实例.
因为您的NSCell
子NSCell
类在其组合中包含其他子类实例,所以它们不再直接从NSControl
视图层次结构中的实例接收这些事件消息.因此,为了使这些单元实例从事件响应器链(视图层次结构)接收事件消息,您的单元实例需要传递这些相关事件.您正在重新创建NSView
层次结构的工作.
这不一定是坏事.通过复制NSControl
(及其NSView
超类)但在NSCell
表单中的行为,您可以按位置,事件类型或其他条件过滤传递到子单元格的事件.缺点是复制了NSView/NSControl
构建过滤和管理机制的工作.
因此,在设计界面时,您需要考虑NSButtonCell
(和NSTextFieldCell
s)NSControl
在普通视图层次结构中的s或者子NSCell
类中的子单元格中是否更好.这是更好地利用其已经在一个代码库中存在,你的功能,而不是重新发明它(并在以后继续保持它)不必要的.