Changing view frame size - macos

Working on https://github.com/madebybright/Nimble/
Trying to change the view frame size when the query function is run, but I keep getting
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.
when the query function runs.
When assigning the size in viewDidLoad everything works fine, but in query (https://github.com/madebybright/Nimble/blob/unstable/Nimble/MenuViewController.swift#L43) the error is thrown.
Any idea why?

Yes. It's because
This application is modifying the autolayout engine from a background thread.
:)
To be more specific, you must do all UI-related work on the main thread. You can use dispatch_async to enqueue a task (such as your frame-size adjustment) on the main thread.

Related

Updating an NSView with processing in a separate thread

Suppose you have a scrollable NSView. When you scroll it, the part coming into the clipping rectangle will need to be refreshed, and the view's drawRect (Swift draw) method will be called for a rectangle containing that new part. Suppose now that before that part can be drawn, some time-consuming processing will need to be done. Since you don't want to burden the main thread (from which drawRect is called) with that processing, you might want to unload it on a secondary thread. Now my question is, what's a good way to do this? For example, you can:
Initiate asynchronous processing from drawRect, exit it without drawing, and have the secondary thread, when it's finished, store the data somewhere and call setNeedsDisplay and thus have drawRect get called again.
As above, but when the processing is finished, draw into the view directly (in the main thread), without going through drawRect.
Set canDrawConcurrently for the view beforehand; but that, as far as I can tell, will not necessarily force drawRect into a secondary thread, unless the system decides to do so.
Or, maybe some other way? This seems like a common enough situation, but I can't find any tips on how to do this in a clean way.

performSelector afterdelay still running even though I have unloaded the viewcontroller that it originated from

In Xcode I have a view which does a modal segue to tab bar controller, and the first view in the tab bar is a UIViewController. This view performs a task on a timed basis (every 15 seconds) using [self performSelector:#selector(blah:) withObject:nil afterDelay:15]. I have some delegate methods that take me back to the root view and calls [self dismissViewControllerAnimated] which in my mind would unload the tab bar controller which would in turn unload the view that is performing the selector every 15 seconds. While those views do disappear after I call dismissViewControllerAnimated, the selectors continue to run every 15 seconds (I see them logging messages in the Xcode console). Am I wrong in my logic that these selectors should cease since their viewcontroller has been unloaded?
Not always. It depends on your classes, if you have a strong reference to the controller or the timer somewhere it may not actually be released. It could also be that since the timer was never invalidated it's keeping the view controller from releasing. Timers are interesting in that they can cause behavior like this if not invalidated, it's always good practice to clean them up when you're done with them. If you're using recursion to call the timed method on itself every 15 seconds that could also keep the view from being released.
If you're using a timer you'll want to invalidate it when appropriate, on view disappear probably. If you want to run once after view disappear you'll need to set a bool so you know when the view disappeared so you can run it once more.
If you're using recursion, be really careful. Something like that can run on a background thread indefinitely. Again if you only want it to run while the view is the active view you can do a check to see which view is on top and only call the method recursively in that scenario, or set a bool on appear and disappear and check the bool when the method calls itself.

drawRect suddenly stops being called

Using XCode 5.1 with custom OpenGL view.
I have a separate thread that processes data and calls setNeedsDisplay to refresh the display.
This is working well, but sometimes after running for a while and using other apps, when I return to my app, drawRect of the OpenGL view is stopped being called.
I see that setNeedsDisplay is being called and, drawRect is called whenever I resize the window.
Can anyone suggest a solution or at least a way to track down the reason for the sudden change in this behavior?
To whoever might be interested:
Seems like calling setNeedsDisplay from another thread might cause problems, together with a high frequency of these calls.
I've significantly lowered the amount of calls to setNeedsDisplay and changed the calls to performSelectorOnMainThread and it seems to solve the probem.

Layer backed NSOpenGLView + animation timer = strange drawing behavior?

I am creating an application which subclasses NSOpenGLView to do some OpenGL drawings. Now i wanted to add an overlay control, similar to QuickTime X. First thing i tried was setting NSOpenGLCPSurfaceOrder to -1 and making my window none-opaque. It did work, but i lost the windows shadow, so not a viable solution.
I then made my NSOpenGLView subclass layer-backed by calling [setWantsLayer:YES] and adding my control box as a subview. It worked, but i noticed a big drop in performance. I did some research and found this:
I am using an NSTimer object to call a method [timerFired:] 60 times per second. Works perfectly. The only thing i do within this method is calling [self setNeedsDisplay:YES], because when using a layer-backed OpenGLView one needs to overload the [drawRect:rect] method and do all the OpenGL drawing there. The problem: [drawRect:rect] often gets called although the timer didn't fire.
At first i thought "of course it does, it draws the NSOpenGLView, so it might be called by the window manager or something". So deleted my timer object to determine how it was called between [timerFired:] calls. The result: It wasn't called at all, at least not when not resizing or dragging the window.
So next i experimented with my timers time interval. Turns out, up until 55 times per second the [drawRect:rect] is called between 60 and 70 times per second, and from a timer interval of 59 times a second onward the [drawRect:rect] is called between 100 and 120 times a second.
I suspect that this highly unpredictable manner in which my drawing is called leads to the performance loss, either by being uneven or by clogging the thread with a lot of OpenGL drawings and not enough time to be executed or something. I also read that layer-backed OpenGLViews don't work with vsync, although i can't confirm this.
Does anyone have an explanation? Does anyone have an idea on how to only draw my OpenGL on timer fires?
I already tried the naive approach and added a boolean variable, set it true in my [timerFired:] and made my [drawRect:rect] only calling the OpenGL drawing method if the variable is true. The result was an extremely flickery and stuttery animation, so no luck.
What about using an CAOpenGLLayer with asynchronous animation oder CVDisplayLink? Would either help?
edit another thing which might be helpful information: I already used [setNeedsDisplay:YES] without my view being layer-backed, so i could easily switch between layer-backed and not-layer-backed, and i didn't have any of the problems described above, so it definitely has to do with my view being layer-backed. Everything gets a mess by just calling [setWantsLayer:YES].
So i replaced my NSTimer object with a CVDisplayLink and everything is smooth again. Although i don't actually understand why. It seems like requesting a backing layer broke the vertical sync, but it does not explain the random calls to drawRect that appeared out of nowhere.
Also i would really like to if vsync works with a layer backed NSOpenGLView and / or with a CAOpenGLLayer. I can't find any official information.

Laggy interface with NSSearchField hooked up to an NSArrayController via bindings

So I've got an NSSearchField hooked up directly to an NSArrayController via bindings, attached to the filterPredicate, so that without any code, the user can just type in the NSSearchField and filter the list of objects in the NSArrayController presented to him in the interface (an NSCollectionView, to be specific).
The NSSearchField is hooked up to provide live searching, so that the NSCollectionView is filtered instantly as the user types, not after waiting for a short period for the user to stop typing.
However, the problem is that this makes the interface really laggy. Typing is delayed significantly, by 0.5-1 seconds, and it seems like the NSCollectionView is trying to animate each and every rearrangement of items for each portion of the search string that the user enters.
What I'd like is for the searching to be live, but the typing in the search field to be fluid, and the results to filter as fast as possible. Is there a way to do this via bindings, or will I need to put in some custom code that triggers the filterPredicate on a separate thread?
(Note that I've got a custom sorting algorithm set up on the NSArrayController, and removing it seems to help a bit with the laggyness, but not completely.)
I would definitely go with the predicate on the separate thread. It seems you know what you have to do. Obviously blocking on the current thread is the lag issue.
Actually, it looks like you can't call setFilterPredicate: from a separate thread. It causes a crash.
It turns out that my problem was actually caused by some slow code being called over and over when setting the filter predicate, which severely slowed down the performance of the filtering. I found this by using a Time Profiler tool in Instruments on my app. That showed me which method was taking the most time, and optimizing that method fixed the lagginess issue.

Resources