I have a subclassed NSTextView that I am manipulating in a separate thread (using performSelectorOnMainThread:withObject:waitUntilDone:) using selectors replaceCharactersInRange:withString:, setSelectedRange:, and insertText:. I'm experiencing flickering of text, and poor performance as the NSTextView updates the display for each and every operation.
Any suggestions on how to control when the display is updated, so I can update it only when actually needed? I tried using various combinations setNeedsDisplay:NO (from both the main and background threads, before and after my updates) which seems to be ignored.
Thanks in advance to anyone who can provide some insight.
I think you should be manipulating the underlying NSTextStorage for the text view, rather than invoking the view's event-related methods directly. This is a pretty classic example of a Model-View-Controller architecture: the NSTextView is the view and the NSTextStorage is the model. Whenever possible, you want to manipulate the model directly and let the controller/view layers deal with updating the view as they see fit.
Related
I am aware that Apple is deprecating the use of NSCell in favour of NSView (see AppKit 10.10 release notes). It was previously recommended that NSCell be used for performance reasons when many controls were needed.
I have spent considerable time implementing a custom control that required many subViews and the performance using NSView-type subViews was not good. See related stackoverflow discussion What are the practical limits in terms of number of NSView-type instances you can have in a window? I was struggling with 1000-2000 in-memory objects (which doesn't seem a lot). What is the actual reason for this limitation?
One thing that confuses me on the above is View-based Cocoa NSTableViews. You can create tableViews with more than 1000-2000 cells and they don't seem to have poor loading and scrolling performance? If each of the cells is an NSView then how is this achieved?
If there are practical limits, then what are Apple thinking when they say they are deprecating usage of NSCell's? I am sure they are aware that some controls need a large number of subViews.
Further, an (probably outdated) Apple Developer Guide give the following explanation for the difference between NSView & NSCell which I need explained further:
"Because cells are lighter-weight than controls, in terms of inherited data and behavior, it is more efficient to use a multi-cell control rather than multiple controls."
Inherited data: this would surely only cause "bloat" if the data was being used => and it would only be used it you needed it?
Inherited behavior: methods that you don't use in a class/object surely can't cause any overhead ?
What is the real difference between the lightweight NSCell versus the heavyweight NSView other than that it just seems to be conventionally accepted?
(I would really like to know.)
A brief and incomplete answer:
NSCells are about drawing state, and not much else. NSViews must draw, but also must maintain and update all kinds of other information, such as their layout, responding to user input events, etc.
Consider the amount of calculation that must happen if you resize a view that contains many hundreds of subviews: every single subview position and size must be maintained according to the existing constraints for the view layout. That alone is quickly a very large amount of processing.
NSCells, in contrast, don't exist in a layout in that way. Their only job is to draw information in a given rectangle whenever asked to do so.
One thing that confuses me on the above is View-based Cocoa NSTableViews. You can create tableViews with more than 1000-2000 cells and they don't seem to have poor loading and scrolling performance? If each of the cells is an NSView then how is this achieved?
NSTableViews re-use views. The only views actually generated are those that are visible, plus maybe a row of views above and a row below the visible area. When scrolling the table, the object value associated with a given row of views is changed to that the views in the row will display different content
This question is for both cocoa and cocoa touch. But I'll write an example just for cocoa.
As I understood, I can setNeedsLayout to YES multiple times in a cycle and -layout will be called just once. But are there any other benefits of laying out subviews in -layout method?
Explanation / example: At the moment I'm laying out my subviews in custom viewController (that has default NSView) every time I call custom redraw method. And I call redraw method only when user changes some properties so I really want to relayout subviews.
There are plenty of external circumstances not under your direct control that might cause the system to want to lay out your views. For example, device rotation or incoming calls on iOS, or window resizing on OS X. If you have your layout logic in the standard places, then your code accommodates these without any additional effort, and in the places your internal state changes, you can request such a layout explicitly.
To turn your question around: is there a significant benefit to not doing your layout in the standard way? Do you believe that this will be a performance issue? Have you measured it to see whether it is actually a performance issue?
I have recently started creating my own controls and I seem to have a bit of trouble understanding how I should use drawRect.
Basically I have 3 Questions.
Is it a good idea to have conditional drawRect's? ie. different drawing code based on properties or instance variables.
What is the best method for animating changes to the drawRect's drawing? For example, a fuel gauge control with animated fill and un-fill.
And, finally, the examples I have seen for animating with drawRect tend to use timers, is that really a good method in practice? It seems like the heavier apps would have issues with that method.
I guess a 4th would be, is there, perhaps, a better place to do this kind of stuff?
Is it a good idea to have conditional drawRect's? ie. different drawing code based on properties or instance variables.
Sure, why not? If your drawRect: method becomes unwieldy, you could split it into multiple methods that you then call from drawRect: depending on the properties of your view. E.g. you could have methods like drawBackground, drawTitle, etc.
What is the best method for animating changes to the drawRect's drawing? For example, a fuel gauge control with animated fill and un-fill.
That depends. For very small views, you could call setNeedsDisplay from a timer, but for larger views, you'll often run into performance issues with this approach.
Animating changes is often better done by compositing your view out of multiple subviews or layers that can be animated with Core Animation (or the simplified UIView animation methods).
In MVC the View shouldn't hold it's data. However I know in Objective-c you do: [textField setString:#"hello"];, that string is then retained by the text field. The same applies for the textField's font and text colour, etc.
However a UITableView uses a datasource to ask a controller for it's data, it's then up to the controller to reload the table view. But it also stores some data itself, like background colour.
I can understand a reason as to why a UITextView doesn't use a data source the code would become much more lengthy, if every property had to be a method. But why use a data source in some cases and not others, why not just set an array of UITableViewCells (I know that this means cells could not be reused and so it would use more memory, but what other design reason is there), for the UITableView to display?
And when creating you own objects how do you know when to just store a small amount of generic data (e.g. the string a textview displays can only be a string, but any the string itself can be anything)in a view, or use a datasource?
MVC is a pattern, not an edict. Let the view do the work. Some coupling is just going to happen. Follow the guidelines of the pattern, and bend it to the style and desires of your developers and organization.
I'm not familiar with objective-c's mvc framework, but I think I understand the question.
Basically, you don't want the view doing anything with the datasource backend, that is, anything having to do with the plumbing of accessing the DB.
But its ok for the view to have access and use the data itself. That is the M part of MVC. The model gets passed around. The view knows how to display it. The controller knows how to do the business logic to it (including interacting with backend systems like the data access layer).
In the case of data grid, it has to hit the backend to get the data, so it has to rely on the controller.
Ideally, the view knows only about display related information (like the background color). The whole idea being separation of concerns. You want the view to handle just its part of things, like-wise the controller. Then you can modify them independently of each-other.
As for the specifics of the datasource (versus an array), grids tend to be complex. Maybe that is handling paging or other niceties. In this case, I don't think its so much the separation of layers (since an array could just as easily be the model), but handling more functionality.
I'm not sure what you mean re 'storing' small amounts of data in the view. The view should tend to deal with 'view stuff'.
My app requires an interface that has many buttons, text fields and matrixes. And they need to change from time to time. Right now I do this by having all elements in IB already and hiding/showing/moving them when needed. What would others recommend? Should I do that? Should I use an NSTabView? NSView? Should create the elements programatically? If so, what if I have an element that is already created that I need again without changes? It would be a waste of releasing it and creating it again.
Any help would be greatly appreciated.
In my opinion, it's better to create interfaces programmatically if you have to animate views around a lot. If it's just a matter of hiding/unhiding them, IB works great, but if you need re-layout or create unknown numbers of views dynamically it's not worth trying to make it all work with nib files.
As for general advice:
Create subclasses (from UIView or UIControl or one of their subclasses) for every kind of element you're going to use. It's tempting to piece together composite views from your UIViewController, but you'll really be much better off creating real classes.
Study the standard Cocoa view classes, and try to create similar API:s in your own controls and views.
Put as much data (sub-element positioning etc) into a plist, so that you can easily change it from one centralized place instead of having to dig around in the code.
If you are often creating several dozen short-lived views, it's worth keeping them in a pool and reusing them. But if it's just a few labels being added and removed intermittently I wouldn't worry too much about it. As usual: don't optimize too early.
Your current approach sounds fine. If you're showing/hiding them but otherwise they remain unchanged, why go through the trouble of creating them with code, when your XIB keeps a "freeze-dried" copy of exactly what you need already?
As long as you're keeping them within logical groups, you can just move/swap/show/hide the group's container (like NSBox or an NSView). If you have a LOT of logical groups, which aren't always shown every session, you can separate them out into their own XIBs and only load them when they're needed, to save launch time and memory.
If you use NSViewController, it's even better because you can make clean breaks for each logical group. Load the panel as the view and the view controller will keep outlets/actions and has a one-to-one relationship with a xib.