color reference: working, but much too slow…

Hello,

I’m starting over again with a problem that I had solved in ASOC, but this time in Objective-C.

I have a matrix of customized cells, which have to retain a single information: their background color. A former version was suggested to me by rdelmar: the coding of the color was added to the customized cell and saved to a file (as background color is usually NOT saved).

My new version does this differently: the coding is not necessary, because the color information is retained as a color reference, an integer value stored conveniently into the tag of the cell.

So far, so good: the color reference allows the changing of the color: the color picker changes the color, but not the reference, so the color of the cell can be updated any time.

In my app, I made possible for the user to create, via a controller, any possible number of colors. I have still to implement what happens to cells when a color is deleted, but I can do this later.

My problem is, as the table color can change, be shortened or expanded, as every individual color may change, the only fixed reference is the controller itself. So I decided to put a reference to it into a class variable, like this:

The initialization of the cell is straightforward, I just want a new cell to be white, no border and so on. And I put the reference into the class variable:

Now. for the drawing itself. This is laborious! Each cell must look into the controller to retrieve the color reference and sets its background accordingly. Once again, the code is working but. imagine this: they are 4096 cells! Even with the best processor, this takes too much time:

The color records has the “colorRef” key for the color reference and the “mapColor” key for the color itself. My design seems correct, but the implementation is not responsive enough. Do you have suggestions to speed up the drawing process? Maybe a “pure C array” for the color table, updated only when a color is changed?

Regards,

Hi,

no real help, but a few notes:

+(MapButtonCell *)initWithController: (NSArrayController*) ac

is quite confusing to read

There is a init method naming convention

Instance init methods begin with init[With].


- (id )initWithController: (NSArrayController*) ac 

Class init methods begin with the class name or a part of it


+ (MapButtonCell *)cellWithController: (NSArrayController*) ac 

In a memory managed environment Class init methods should return autoreleased objects


A normal init method should look like


- (id )init
 {
    self = [super init];
    if (self) {
    // do other initializations
    }
    return self;
}

To retrieve a value from a dictionary, use objectForKey: rather than valueForKey:


 for (NSDictionary *theDic in [itsController arrangedObjects]){
        if ([[theDic objectForKey: @"colorRef"] integerValue] == self.tag)
           theColor = [theDic objectForKey: @"mapColor"];
    }

Hi Stefan,

Thank you for the naming convention, I’ll change this.

I’m lost with this idea, I’m pretty sure it is not bad, but the design is not good. The idea of storing the colors in a class color table does not improve the drawing speed:

The:

takes too much time. I wonder how a Matrix is redrawn – a NSLog shows that only the changed cells are redrawn, but it takes ages to refresh on screen.

Curses.

willDisplayCell:. might be faster, consider also it’s not necessary to refresh the whole table view.
There are methods to refresh only an index set of rows.

I’d like to repeat the option to use a custom class instead of pure dictionaries and move some intelligence into the instances.

Or even CoreData.

Stefan,

The standard dictionary fits perfectly here, as there is only an “AS Record” storing four keys. The rebuilding of the color table takes a non-mesurable time (there are only four or five colors, and I doubt there will be more.

The problem is elsewhere. The ASOC version of the matrix was much more faster (I just can’t understand why).

I’ve started reading about Core Data and, to put it briefly, I’m not ready to wrestle with it. I don’t even seize the notion of “context” by now.

I suppose my matrix is updating much more as needed. Some operations are called repeatedly and uselessly, but which ones? is there a “basic” draw method for the NSButtonCell that I missed? I just want to set the background color and nothing else. Do I have to validate a region after drawing to prevent useless redrawing? When the matrix is drawn for the first time, 4096 custom cells are initialized, they look at the color table and draw in half a second! Then, if I change one single cell, it takes more than 5 seconds.

I’m still lost. And here comes the time I begin to change all the design and nothing more is working. :o

PS: there is no “willDisplayCell.” method for the NSMatrix.

It simply does not speed up, whatever I try to do.

For these of you who have tested it, is a NSCollectionView faster than a NSMatrix? I have seen two or three examples, but of course they show how to put nice things in the collection, like buttons and icons, so the simple of the NSCollectionView is a bit masked by the complexity of the result: sorting icons, resizing windows, gradient background.

I just want a collection of 64x64 square views, which just set their background color and draw nothing more. Usually, examples show something simple and let the user make something more complex – here I have complex examples that I need to simplify.

So, question: for the grid I want to implement (fixed to 64x64, no resizing, no scrolling) what is the best class: NSMatrix or NSCollectionView?

Help me, please, because I’m running out of sleep :rolleyes:

I can’t quite see what you’re after, but this bit of the docs strikes me as pertinent:

The only other suggestion I’d make is to consider a single subclassed NSView, with its drawRect: doing all the work. You might even want to look at Quartz 2D.

I’ve worked with custom NSMatrix in the past and custom cells without problems. When matrices become noticeable slower you’re doing something wrong (with cells). Your posts are still too general for me but the most common problem is subclassing cells. The advantage of a cell is that it can display itself on the screen hundreds of times per second without problems and without needed to copy or instantiate itself (the drawback of a view).

I’m pretty sure I make something wrong: the first time the NSMatrix is drawn, I make (for testing) every custom cell set its background to [NSColor blueColor]. The result is: the matrix is drawn quasi instantaneously! And there is nothing more later: I select some cells with the mouse, like this:

-(void) mapClick{
int theTag = [[[[mapController selectedObjects]lastObject]objectForKey:@“colorRef”]integerValue];
for (id aCell in [mapMatrix selectedCells]){
[aCell setTag:theTag];
}
}
Setting the tag is (of course) very fast again. Then. the matrix begins to resemble to an asthmatic sloth, as there was intensive calculations or memory operations, which shouldn’t be.

To Shane:

implement such a NSView subclass (the matrix as a whole) with a sort of “square pencil” to draw the “cells”, using a memory matrix to retain the colors (sort of mapColor [0…63, 0…63]) was the solution I used, but it was 20 years ago in procedural Pascal. I was hoping that Cocoa could preserve me the redo such a thing.

Should I post the whole program? It is not so big. Here is the custom cell:

MapButtonCell.h:

MapButtonCell.m :

Regards,

I replaced the initialization of the cell by an instance method, and left the table rebuilding as a class method.

  • (void)loadColorTable: (NSArrayController*) ac;
  • (MapButtonCell *)initMapCell;
  • (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;

. this is “normally fast” now. Not as responsive as Photoshop, but much better :confused:

I’m not really understand why you chose this construction but anyway. Have you tried quarzDebugger? It shows you if and where the are drawing issues.

What’s wrong with my subclass? If there a way to make things simpler, I’ll take it.

I just downloaded and tried it. It can’t be possible: Quartz Debug just blinks again and again when I click on the matrix. It is not redrawn twice, but quasi permanently! What makes the NSMatrix constantly redraw itself?

Not SO far anymore. Hey, this thing is incredible! Aaron Hillegass’ tutorial on the bases of Core Data is very clear, I like this step-by-step exercises with screen shots.

All you can have for free (incredible, you even get multiple Undo/Redo) makes me just want to rewrite all my applications using this framework!

But alas, this does not solve my matrix problem. I look toward Quartz now, it reminds me vaguely of the old world QuickDraw, when I had to do everything myself to make it work faster on a Mac Plus…

Any ideas for what I’m doing wrong into the code above? The other sites remain silent too.

Regards,

Shane, I’m on my way. A 20-years old idea is back to life! Of course this idea will cost me a few extra work, like learning Quartz. This is a beginning:

This lovely little thing draws 4096 “virtual cells” in a non-humanly measurable time! The C matrix is used for the color reference.

Maybe I should rebuild a “color array” before drawing, but as “objectAtIndex” method is very efficient, this is not true optimization.

And now: how to get a point clicked into the view, determinate which “cell” was hit, and set the gridData matrix to the correct reference (that is, the selected color on the controller’s list). Quite an interesting exercise.

If someone is understanding what I’m doing (it’s a “paint module” using a grid of 64x64 colorable cells to figure a map) and has a better idea (I mean maybe there is something in Quartz which allows to do this more easily) you are very welcome.

Regards,

To bring back the idea of a custom class:

Each object in the array controller represents one cell.
Each cell is an instance of a custom class containing the key/value pairs of your dictionary.
Additionally it contains an instance of the view (just for the cell, not one view for the whole grid),
the place/coordinates in the grid and a tracking area to detect mouse clicks.

Hi Stefan,

There are many ways to solve the problem.

¢ Shane’s solution is quite easy to implement – in fact, it working perfectly now, I just did this:

Even if you click into the “last” cell [63,63] it is redrawn without delay.

This “bitmap” approach offers the possibility of using tools like rectangle, ellipse, paint bucket.

¢ Other solutions (like yours): a custom view, with an instance variable (NSArray) holding custom views. These views are placed at initialization and may receive clicks directly. Not bad! Each view may hold the very minimal instance variables (tag, for example, is “pre-implemented” in NSView). But tools are less easy to implement.

¢ Mixed solutions (Quartz view + cocoa structures).

I’d like to submit a problem to the MacScripters Gurus (who allowed me to learn so much in a very little time and don’t hesitate to provide precious fragments of working code):

For the moment this “map” is a little test app by itself (it does not do more than present a blank map, allow colors to be defined, then used to fill map’s cells, and finally saved to disk and reloaded.

The next step is to make these objets a part of a Core Data application. So: Core Data is not for the faint hearted, it gives a lot but you have to follow the rules. For instance, data structures (attributes) are managed by array controllers. I already tested a multiple-controllers application (with customized controllers) which works well, excepted that a tiny zombie rascal :o is crashing my app when the last window closes (see my other post).

But whatever structure I choose finally, it has to be compliant with Core Data.

So what would be the better solution? With NSArchiver I can store what I want, but Core Data is more strict (or I missed something).

Any clues, as usual, are really welcome.

I’ll try something so:

So each “cell” has now its “color reference” (I want each color to be editable, so I only keep a reference to it).

In the Core Data document, I create the matrix, and I get the colorReference by the synthesized accessor. I create a dummy ArrayController just to hold this array and to be “Core Data Compliant”:

That’s it. Well, it’s a sort of hybrid structure of Core Data and Quartz, but it should work.

Well, it does not.

I suspect this is a problem with my Core Data entity. I defined an entity called “Map”. It has three attributes:

scale : NSNumber with NSFormatter (mandatory if you want a NSNumber instead of a NSString, which >bangs<).
units : NSString
mapData : Binary Data ???

This mapData attribute causes a problem. My custom view (“matrix” property) stores its color references into a NSMutableArray. This is the array I want to put into the mapData attribute. But this does not work:

Ignore everything but the “lastObject”: this is the (single) Map entity.
[matrix colorReference] is the (property) NSMutableArray.

Now, to simplify: I want the NSMutableArray to become the “mapData” attribute of the Map entity, to be stored when the doc is saved, and later retrieved and transferred to the custom view. The design is clear, the way to do it much less.

Help me, please.

I don’t know that any of the regulars here ever use CoreData. Certainly the comments by those who know far more than me made me shy of it…

Shane, I think there is a lot of people on earth that knows much more than me about Core Data.

This framework is a trap :slight_smile: : you follow the tutorials, like “recipes”, “department and employees”, everything seems simple as long as you stay in IB. One tableView here, some simple bindings there, “entities”, “attributes” are not so scary, you do not worry too much about these complicated words like “managedObjectContext”, you paste them in the right place, and you marvel at an interface that runs by itself.

And then you want something a little more personalized, you start coding, and you begin to regret very quickly NSArchivers…

You realize that literature is enormous, that the sites are full of questions about iOS (this iOS frenzy…) and that the only ones who could help are experts who say “it’s simple, just…” . Others, like me, go bump in the night, under the pale light of the docs from Apple engineers.

Challenging, isn’t it? :slight_smile: