2

好的,基本上我有一个带有一系列稍微重叠的子视图的视图。当一个子视图被点击时,它通过一个非常简单的两条线移动到顶部(除其他外):

-(void)mouseDown:(NSEvent *)theEvent {
  if (_selected) { // Don't do anything this subview is already in the foreground
    return;
  }
  NSView *superview = [self superview];
  [self removeFromSuperview];
  [superview addSubview:self positioned:NSWindowAbove relativeTo:nil];
}

这工作正常,似乎是做到这一点的“正常”方式。

问题是我现在在视图中引入了一些拖动逻辑,所以我需要响应-mouseDragged:. 不幸的是,由于视图已从视图层次结构中删除并在此过程中重新添加,因此我不能让视图进入前台并在相同的鼠标操作中被拖动。它接收mouseDown:,但从不接收mouseDragged:或任何后续事件。如果我离开鼠标并再次单击视图,它只会拖动,因为第二次单击不会进行任何视图层次结构的调整。

我能做些什么来让视图像这样移到前台,并继续接收后续事件-mouseDragged:-mouseUp:事件?

我开始考虑在超级视图中覆盖-hitTest:并拦截 mouseDown 事件,以便在视图实际接收到事件之前将其带到前台。这里的问题是,从内部-hitTest:,我如何区分我实际执行命中测试的事件类型?我不想将子视图移动到其他鼠标事件的前台,例如 mouseMoved 等。

更新 | 在我的超级视图中,我实际上已经做到了这一点,但感觉就像是一个非常糟糕的代码气味,-hitTest:直接触发了一个事件。

-(NSView *)hitTest:(NSPoint)aPoint {
    NSView *view = [super hitTest:aPoint];
    if ([view isKindOfClass:[EDTabView class]] && ![(EDTabView *)view isActive]) {
        // Effectively simulate two mouseDown events in sequence for this special case
        [view mouseDown:nil]; // Works because I know too much about the implementation
    }
    return view;
}

这有两点感觉不对。最大的是我在执行之前-hitTest:甚至返回之前执行的操作,然后在-hitTest:完成后执行 AppKit 处理的操作。第二个是我没有要传递的事件,所以我传递的是 nil。它在这种情况下有效,因为我知道事件处理程序实际上并不处理任何事件数据,但这并不是很好。另一种方法是将整个逻辑移到超级视图中,但这里的关注点分离感觉更糟。

更新 | 这个-hitTest:黑客被破坏了......当我没有点击鼠标时它会被触发,例如当我将某些东西拖到视图顶部时。仍在寻找解决此问题的好方法。

答案可以从字面上指示视图如何在拖动操作期间离开并重新进入其父视图,或者(也许更优雅)将视图移动到前景的另一种方法,而不是[view removeFromSuperview]; [superview addSubview:view ...];.

4

3 回答 3

4

好吧,我一时兴起,尝试了一些不合逻辑的事情。

而不是将视图移动到前景:

[theView removeFromSuperview];
[theSuperview addSubview:theView positioned:NSWindowAbove relativeTo:nil];

我只是放弃了这removeFromSuperview条线,想知道如果您尝试将视图添加到超级视图两次,AppKit 实际上会做什么(我认为它可以防止它发生)。

它至少在 Snow Leopard 下有效,并且解决了很多问题。正如方法名称所暗示的那样,已经存在于超级视图中的视图不会被添加,但它确实可以设置其位置,而无需先将其从超级视图中删除。

于 2011-01-02T11:53:42.717 回答
0

要以编程方式触发鼠标事件,您可以使用Performing a double click using CGEventCreateMouseEvent()

CGEventRef ourEvent = CGEventCreate(NULL); //save the mouse event to track location
PostMouseEvent(kCGMouseButtonLeft, NX_LMOUSEDOWN, CGEventGetLocation(ourEvent));

void PostMouseEvent(CGMouseButton button, CGEventType type, const CGPoint point)
{
   CGEventRef theEvent = CGEventCreateMouseEvent(NULL, type, point, button);
   CGEventSetType(theEvent, type);
   CGEventPost(kCGHIDEventTap, theEvent);
   CFRelease(theEvent);
}

如果您有很多内容在移动,您甚至应该CGEventCreate(NULL);在操作之前调用,甚至可以执行以下操作:

CGEventRef ourEvent = CGEventCreate(NULL);

//Some long operation

CGEventRef dragEvent = CGEventCreate(NULL);
PostMouseEvent(kCGMouseButtonLeft, NX_LMOUSEDOWN, CGEventGetLocation(ourEvent));
PostMouseEvent(kCGMouseButtonLeft, NX_LMOUSEDRAGGED, CGEventGetLocation(dragEvent));

这给了用户拖放项目的错觉,即使它们从一个超级视图移动到另一个。

于 2014-01-20T14:27:40.530 回答
0

我找到了你的帖子,因为我遇到了同样的问题(很烦人,顺便说一句)。正确的解决方案是使用您自己的特殊比较器功能对子视图进行排序。在父视图上,调用 -sortSubviewsUsingFunction:context: 方法。

于 2012-03-28T17:14:46.167 回答