问题
In a program I am working on I am performing a large amount of data crunching, and want to be able draw a custom NSView to represent the process visually.
The data crunching is processed off the main thread by using GCD (using dispatch_sync as the order needs to be preserved):
for (int i=0; i<iterations; i++) {
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Add a data crunching task to the queue!
});
}
When the processing is started I initialise a timer, which is used to ask the custom display to redraw at regular intervals (this timer is invalidated once all of the processing tasks are completed):
-(void)onTick:(NSTimer *)timer {
[_customView setNeedsDisplay:YES];
// Update some other UI object (labels etc.)
}
I have this working as expected, however the only problem I notice now is that if I do something like click and hold on a button on the same window, or begin dragging a slider while the process is going on, the redrawing of the custom view is prevented until I let go of the mouse!
Is there any way I can prevent this from happening, preferably without moving all of the controls onto a separate window!
回答1:
yip the timer is on the runloop and while in the drag drop / tracking mode it doesnt get fired because the runloop isnt advanced for the common mode
add it to the modes needed:
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(myTimerAction:) userInfo:nil repeats:YES];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode: NSEventTrackingRunLoopMode];
edit: the runloop is basically apple's while loop for dispatch ANY event to an app. and it filters the events it dispatches based on so called modes.
回答2:
Thank you all for the question, answers and comments.
Use of NSEventTrackingRunLoopMode has solved my case that constantly redrawing an OpenGL view with given parameters.
class MyOpenGLView: NSOpenGLView {
var timer: Timer?
required init?(coder: NSCoder) {
super.init(coder: coder)
timer = Timer.scheduledTimer(withTimeInterval: 1.0/60, repeats: true) { (t) in
self.needsDisplay = true
}
let runloop = RunLoop.current
runloop.add(timer!, forMode: .commonModes)
// runloop.add(timer!, forMode: .eventTrackingRunLoopMode)
}
override func draw(_ dirtyRect: NSRect) {
// draw moving 3D-cubes
}
}
Now it keeps drawing even I am dragging a slider to update its parameters. Additionally, resizing window has become smooth.
For other solutions "Do it asynchronously," interesting! I will give it try later. Thanks!
EDITED:
I have changed my mind to use NSRunLoopCommonModes (RunLoopMode.commonModes), instead of NSEventTrackingRunLoopMode.
Both works well, but Apple's document implies that Common Modes is better in my case.
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
来源:https://stackoverflow.com/questions/13933902/can-i-redraw-a-custom-nsview-while-another-control-on-the-same-window-is-being-u