NSWindow 移动期间的通知

发布于 2024-10-21 05:04:54 字数 147 浏览 5 评论 0原文

当通过拖动标题栏来更改 NSWindow 的位置时,如何获得通知? 我知道我可以使用 windowWillMove:windowDidMove: 通知,但这些通知仅在拖动开始或完成时才会给我通知。

How can I get a notification while a NSWindow's position is changed by dragging its titlebar?
I know I can use the windowWillMove: and windowDidMove: notifications, but those will give me a notification only when a drag is started or finished.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

浪推晚风 2024-10-28 05:04:54

我有一个解决方案,允许您在拖动窗口时确定窗口的位置。

这两个问题是,在拖动窗口时没有内置方法可以收到通知,并且窗口的框架在停止移动之前不会更新。我的方法通过设置重复计时器并跟踪光标的位移来解决这些问题。

首先,订阅 NSWindowWillMoveNotificationNSWindowDidMoveNotification 以确定窗口何时开始和停止移动。

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(windowWillMove:)
                                             name:@"NSWindowWillMoveNotification"
                                           object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(windowDidMove:)
                                             name:@"NSWindowDidMoveNotification"
                                           object:nil];

当窗口即将移动时,记录光标的位置并启动一个重复计时器来调用您自己的“窗口正在被拖动”方法。

- (void)windowWillMove:(NSNotification *)notification {
    if (notification.object == self.view.window) { // make sure we have the right window
        self.dragCursorStartPos = [NSEvent mouseLocation];
        const NSTimeInterval dragDelaySeconds = 0.1; // polling rate delay
        self.dragWindowTimer = [NSTimer scheduledTimerWithTimeInterval:dragDelaySeconds
                                                                target:self
                                                              selector:@selector(myMethod)
                                                              userInfo:nil
                                                               repeats:YES];
    }
}

当窗口完成移动后,停止重复计时器。

- (void)windowDidMove:(NSNotification *)notification {
    if (notification.object == self.view.window) { // make sure we have the right window
        if (self.dragWindowTimer != NULL) {
            [self.dragWindowTimer invalidate];
            self.dragWindowTimer = NULL;
        }
    }
}

现在,聪明/黑客的部分是,我们通过计算光标相对于其起始位置的位移并将该位移添加到框架报告的原点(自窗口启动以来没有改变)来确定框架的实际位置移动。

- (void)myMethod {
    NSPoint cursorPos = [NSEvent mouseLocation];
    NSPoint cursorDisplacement = NSMakePoint(cursorPos.x - self.dragCursorStartPos.x, cursorPos.y - self.dragCursorStartPos.y);
    CGPoint frameOrigin = self.view.window.frame.origin;
    CGPoint actualFrameOrigin = CGPointMake(frameOrigin.x + cursorDisplacement.x, frameOrigin.y + cursorDisplacement.y);
    NSLog(@"The frame's actual origin is (%f, %f)", actualFrameOrigin.x, actualFrameOrigin.y);
}

myMethod 中的 actualFrameOrigin 点将报告框架的实际位置,即使 self.view.window.frame.origin 点仅在以下情况下更新:你停止拖动窗口。

这种方法可以让您在拖动窗口时收到通知,并告诉您其实际位置,这样就一切就绪了!

我发现的唯一问题是,快速按标题栏而不移动光标将触发 NSWindowWillMoveNotification,但不会触发 NSWindowDidMoveNotification,这会导致计时器错误地持续重复。为了处理这种情况,我们可以通过检查 if (pressedButtons & (1 << 0)) == (1 < ;<0)。如果没有按住按钮,我们就取消计时器。

I have a solution that allows you to determine the window's position while it is being dragged.

The two issues are that there's no built-in way to get notified while the window is being dragged and that the window's frame doesn't update until it stops moving. My approach works around these issues by setting up a repeating timer and keeping track of the cursor's displacement.

First, subscribe to NSWindowWillMoveNotification and NSWindowDidMoveNotification to determine when the window starts and stops moving.

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(windowWillMove:)
                                             name:@"NSWindowWillMoveNotification"
                                           object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(windowDidMove:)
                                             name:@"NSWindowDidMoveNotification"
                                           object:nil];

When the window is about to move, record the position of the cursor and start a repeating timer that calls your own "window is being dragged" method.

- (void)windowWillMove:(NSNotification *)notification {
    if (notification.object == self.view.window) { // make sure we have the right window
        self.dragCursorStartPos = [NSEvent mouseLocation];
        const NSTimeInterval dragDelaySeconds = 0.1; // polling rate delay
        self.dragWindowTimer = [NSTimer scheduledTimerWithTimeInterval:dragDelaySeconds
                                                                target:self
                                                              selector:@selector(myMethod)
                                                              userInfo:nil
                                                               repeats:YES];
    }
}

When the window is finished moving, stop the repeating timer.

- (void)windowDidMove:(NSNotification *)notification {
    if (notification.object == self.view.window) { // make sure we have the right window
        if (self.dragWindowTimer != NULL) {
            [self.dragWindowTimer invalidate];
            self.dragWindowTimer = NULL;
        }
    }
}

Now, the clever/hacky part is that we determine the frame's actual position by calculating the cursor's displacement from its starting position and adding this displacement to the frame's reported origin, which hasn't changed since the window started moving.

- (void)myMethod {
    NSPoint cursorPos = [NSEvent mouseLocation];
    NSPoint cursorDisplacement = NSMakePoint(cursorPos.x - self.dragCursorStartPos.x, cursorPos.y - self.dragCursorStartPos.y);
    CGPoint frameOrigin = self.view.window.frame.origin;
    CGPoint actualFrameOrigin = CGPointMake(frameOrigin.x + cursorDisplacement.x, frameOrigin.y + cursorDisplacement.y);
    NSLog(@"The frame's actual origin is (%f, %f)", actualFrameOrigin.x, actualFrameOrigin.y);
}

The actualFrameOrigin point in myMethod will report where the frame actually is, even though the self.view.window.frame.origin point only updates when you stop dragging the window.

This approach lets you get notified while the window is being dragged and tells you its actual position, so you're all set!

The only issue I've found is that pressing the title bar quickly without moving the cursor will fire NSWindowWillMoveNotification but not NSWindowDidMoveNotification, which causes the timer to incorrectly keep repeating. To handle this case, we can check if the left mouse button is being held down in myMethod by checking if (pressedButtons & (1 << 0)) == (1 << 0). If the button is not held down, we just cancel the timer.

溺渁∝ 2024-10-28 05:04:54

我绝对不是 Cocoa 专家,但据我所知,即使您仍在拖动并稍事休息,windowDidMove 也会发出通知(鼠标左键仍然按下,鼠标在半秒左右没有移动) )。

观察两件事怎么样:您知道窗口拖动开始,并且您知道它何时完成。观察鼠标移动之间的时间,然后你就得到了移动窗口位置。

I'm absolutely no Cocoa expert, but AFAIK windowDidMove gives a notification even when you are still dragging and just take a little break (left mouse button still pressed, mouse is not moved for half a second or so).

What about watching two things: You know that the window drag starts and you know when it is finished. The time between watch the mouse movement, then you got your moving window position.

友谊不毕业 2024-10-28 05:04:54

我建议查看此链接:
http://www.cocoabuilder .com/archive/cocoa/31183-nswindow-not-updating-position-when-being-dragged.html

回答者说可以使用 windowWillMove 事件来启动计时器必须调用 updateWindow (这似乎是这里的关键),然后您可以定期读取应该更新的框架属性,然后在 windowDidMove 处停止计时器。

I would recommend looking at this link:
http://www.cocoabuilder.com/archive/cocoa/31183-nswindow-not-updating-position-when-being-dragged.html

The answerer is saying that one can use the windowWillMove event to start a timer that has to call updateWindow (which seems to be the critical thing here) then you can periodically read the frame property which should be updated, and then stop your timer at windowDidMove.

南七夏 2024-10-28 05:04:54

Swift 5解决方案⤵︎

// Drop into `applicationDidFinishLaunching`
NotificationCenter.default.addObserver(
    self,
    selector: #selector(didChange),
    name: NSWindow.didChangeScreenNotification,
    object: nil
)

// Drop into same class
@objc func didChange() {
    print("Changed")
}

Swift 5 solution ⤵︎

// Drop into `applicationDidFinishLaunching`
NotificationCenter.default.addObserver(
    self,
    selector: #selector(didChange),
    name: NSWindow.didChangeScreenNotification,
    object: nil
)

// Drop into same class
@objc func didChange() {
    print("Changed")
}
回首观望 2024-10-28 05:04:54

对我来说,依靠 windowDidUpdate 就足够了,并且没有像其他建议的解决方案那样设置计时器。所以在我的自定义窗口委托中我只是这样做了:

- (id) init {

    self = [super init];
    self.currentFrame = self.window.frame
}


- (void) windowDidUpdate:(NSNotification *)notification {

    if (CGRectEqualToRect(self.currentFrame, self.window.frame) == FALSE) {
    
        // Update size and position
        self.currentFrame = self.window.frame;

        //Do window position change stuff here
        //------------------------------------

}

For me relying on windowDidUpdate was enough and no timer was set up as for the other proposed solutions. So in my custom window delegate I just did this:

- (id) init {

    self = [super init];
    self.currentFrame = self.window.frame
}


- (void) windowDidUpdate:(NSNotification *)notification {

    if (CGRectEqualToRect(self.currentFrame, self.window.frame) == FALSE) {
    
        // Update size and position
        self.currentFrame = self.window.frame;

        //Do window position change stuff here
        //------------------------------------

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文