1

I have a simple iphone application that paints blocks using a subclass of CALayer and am trying to find the best way to save state or persist the currently created layers.

I tried using Brad Larson's answer from this previous question on storing custom objects in the NSUserDefaults, which worked for persisting my subclass of CALayer, but not it's basic state like geometry and background color, so they were there but did not render on relaunch.

I made my declared instance variables conform to the NSCoding protocol but do not know how to make CALayer's properties do the same without re-declaring all of it's properties. Or is this not the correct/best approach altogether?

Here is the code I'm using to archive the array of layers:

[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:viewController.view.layer.sublayers] forKey:@"savedArray"];

And here is the code I'm using to reload my layers in -viewDidLoad:

    NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];

NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:@"savedArray"];

if (dataRepresentingSavedArray != nil) {

    [self restoreStateWithData:dataRepresentingSavedArray];

And in -restoreStateWithData:

    NSArray *savedLayers = [NSKeyedUnarchiver unarchiveObjectWithData:data];

if (savedLayers != nil) {

    for(layer in savedLayers) {

        [self.view.layer addSublayer:layer];

    }

    [spaceView.layer layoutSublayers];      
}
Community
  • 1
  • 1
Joe Ricioppo
  • 812
  • 2
  • 10
  • 22

2 Answers2

5

Just to be precise, according to the Apple docs, the CALayer is actually the model and not the view in the MVC pattern.

"CALayer is the model class for layer-tree objects. It encapsulates the position, size, and transform of a layer, which defines its coordinate system."

The view behind the layer is actually the view part of the pattern. A layer cannot be displayed without a backing view.

It seems to me that it should be a perfectly legitimate candidate for data serialization. Take a look at the KVC Extensions for CALayer. Particularly look at:

- (BOOL)shouldArchiveValueForKey:(NSString *)key
Matt Long
  • 24,438
  • 4
  • 73
  • 99
  • This comment is outdated :-). The latest iPhone SDK also supports archiving CALayers. – jarjar Sep 13 '10 at 00:06
  • CALayer is a model class for layer trees, that is right, but that does not in any way make it part of the MVC-pattern model. The model in MVC is only your applications data and logic. – Ahti Feb 19 '13 at 02:33
3

Cocoa (and Cocoa Touch) are mostly based on a model-view-controller organization. CALayers are in the view tier. This leads to two questions:

  1. What part of your model does your layer present to the user?
  2. How do you make that part of the model persist?

The answers to those questions are your solution.

Nowadays, for a new app, the simplest (certainly most extensible) path to a persistent model is probably Core Data.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • 2
    Peter's point here is key. Focus on the data you want to store, not the CALayer implementation. The CALayer should *display* your data, not *be* your data. When you've created a data class, storing that should be fairly straightforward, and when you load it again, you can easily regenerate a new CALayer to display it. – Rob Napier Jul 22 '09 at 03:59
  • You start out right, but then you appear to start stuffing presentation details in your model. I'm not sure what your app is; if it's a graphics app, then maybe “frame” is a legitimate part of the model. Otherwise, I doubt it. If frame rectangles and corner radii and colors are not part of the data that the user is using your application to curate and edit, then they do not belong in the model, and you should leave them to your subclass of CALayer to handle. Your subclass of CALayer should also have one other property, by which you give the layer the model object that it should present. – Peter Hosey Jul 23 '09 at 23:40
  • The controller owns and creates both the model objects and the view objects (layers), and gives each model object to the view (layer) that will display it. Controller and view (layer) share the same model object, and each retain it. – Peter Hosey Jul 24 '09 at 19:01
  • “I pretty much got it working except UIColor will not encode, archive, then un-archive successfully. Is there a way to archive structures, so that I could just persist the CGColorRef without converting it to a UIColor?” CGColor isn't a structure. UIColor's documentation says that it conforms to NSCoding, so whatever archiver and unarchiver you're using should be able to encode it. If that isn't working, you should file a bug in RadarWeb. – Peter Hosey Jul 24 '09 at 19:03
  • Ok thanks. I'll file a bug on UIColor. But what is a CGColorRef if it's not a struct? and is there a way to save it? – Joe Ricioppo Jul 24 '09 at 20:59
  • It's a Core Foundation object. These are interchangeable with Cocoa objects; you can put them in NSArrays (which are also CFArrays), send them `retain`, `release`, and `autorelease` messages, and pass them to the `%@` formatter of `stringWithFormat:`, `CFStringCreateWithFormat`, and `NSLog`. However, they do not conform to `NSCoding`, so you can't archive them directly. – Peter Hosey Jul 24 '09 at 22:39
  • Thanks Peter. I haven't given the CALayer subclass an instance of the model yet, but this is what I have so far which seems like it should work if I was doing it right. http://stackoverflow.com/questions/1182691/archiving-and-unarchiving-results-in-bad-access – Joe Ricioppo Jul 25 '09 at 18:36