Save subclass of SKShapeNode using NSCoding - sprite-kit

I have a subclass of SKShapenode with 2 extra attributes
#property(assign)float size;
#property(assign)float weight;
But when I save a NSMutableArray, with core data, containing some subclasses of my node, the information about size + weight is lost.
newLevel.tractorObjects = [NSKeyedArchiver archivedDataWithRootObject:arrayTractorObjects];
How can I solve this problem?

Since you appear to be using NSCoding to encode the nodes, you need to implement NSCoding in your subclass. Right now SKShapeNode is doing all of the encoding, but it doesn't know about your subclass attributes. To get your attributes encoded, you need to implement encodeWithCoder and initWithCoder. Both of these should call super's implementation so that SKShapeNode can encode itself, and then add/extract your own attributes.

Related

NSCoding with Nested Custom Objects?

I have a series of nested objects that I am needing to put through the NSCoding protocol so that I can save the top level object into NSUserDefaults.
Here is the structure of objects:
'Instructor' class
NSMutableArray that holds instances of...
'Class' class
NSMutableArray that holds instances of...
'Student' class
Name Property
Number Property
Money Property
I am needing to save an instance of Instructor to NSUserDefaults or to documents for the app. As you can see the Instructor object is holding an array that is then holding instances of a class. That class object is holding instances of students.
Is the NSCoding protocol recursive? What I mean by that is if I add the NSCoding protocol to each class, I could then save an Instructor object and it would recursively encode the contained objects?
Would it then work the same way while decoding? I could just decode the one instructor class and it would recursively decode the objects contained because they also conform to the NSCoding protocol?
How could I go about setting this up?
Yes, you can (and probably should) write your support for NSCoding to be recursive. That's how NSCoding is supposed to work.
When your implement encodeWithCoder, simply call
[coder encodeObject: aProperty forKey: #"propertyName"];
on all your object's properties, including it's container properties.
Then make sure every object in your object's object graph also conforms to NSCoding.
For scalar properties, you can save the scalar value using NSCoder methods like encodeInt:forKey:
To save an object that conforms to NSCoding to user defaults, first convert it to NSData using the NSKeyedArchiver class method archivedDataWithRootObject, then save the resulting data into defaults. Quite simple, really.
NSCoding isn't magic so it will work 'recursively' if your implementation of the encoding and decoding methods tells it to be.
Implement the NSCoding methods and pass the data to be encoded to the encoder. Implement the NSCoding methods in all of your custom classes so that when you encode the array all of the contents can be processed appropriately.
Be sure to call super if the classes superclass also implements NSCoding.
e.g.
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.arrayOfClasses forKey:#"arrayOfClasses"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self.arrayOfClasses = [decoder decodeObjectForKey:#"arrayOfClasses"];
}

Isn't property layer of UIView be readonly?

The property layer of UIView is described in Apple's doc as following:
layer
The view’s Core Animation layer used for rendering. (read-only)
#property(nonatomic, readonly, retain) CALayer *layer
It is obviously that it is readonly. But in my project, why it could be set as following:
NSLog(#"before: %f",self.myView.laye.frame.size.width);
[self.myView.layer setAffineTransform:CGAffineTransformMakeScale(2, 2)];
NSLog(#"after: %f",self.myView.laye.frame.size.width);
//log shows us that the frame is modified
Really confused in this situation. Anyone can help me out? Thanks in advance!
The layer property is read-only, it means you cannot change the layer for another, however the CALayer object contained in the property is not immutable, you can set its own properties.
You cannot do:
self.myView.layer = newLayer;
// equivalent to [self.myView setLayer:newLayer];
But you can do:
[self.myView.layer setAffineTransform:CGAffineTransformMakeScale(2, 2)];
It's the setLayer: selector that you can't use.
CALayer is not part of UIKit. It’s part of the Quartz Core framework
while UIView class is a part of UIKit. You can read the documentation of both to know the differences
UIView inherits from NSObject and CALayer also inherits from NSObject so at the time you are doing: [self.myView.layer setAffineTransform:CGAffineTransformMakeScale(2, 2)];
You are not assigning the layer , you are actually accessing the CALayer class layer properties directly and therefore you can play with position, size, and transformation of a layer, as you can see in CALayer documentation, it allows all these things

How to save my own object with NSKeyedArchiver?

i've got problems using the NSKeyedArchiver/NSKeyedUnarchiver for saving my object.
I added the methods - (id)initWithCoder:(NSCoder *)decoder and - (void)encodeWithCoder:(NSCoder *)encoder. The problem is when i try to save the object it doesn't work correctly.
I could imagine a problem (but I'm not sure if it is the problem ;) ). I have some arrays within my object. They contain more objects (I implemented both of the "Coder"-Methods as well). So does the array call the methods in it's objects?
Any possible solution??
Thanks!!
In the header file indicate that your class will implement the NSCoding protocol, like <NSCoding>
In the encodeWithCoder method you need to encode all the fields you want to save like so:
[encoder encodeObject:array1 forKey:#"array1"];
Then in the initWithCoder method, decode the fields that were encoded:
array1 = [coder decodeObjectForKey:#"array1"];
Be sure that any encoded containers only contain objects that also implement the NSCoding protocol. This could be core classes such as NSString, NSNumber, NSArray, NSDictionary, as well as your own custom object.
If your project is not using garbage collection you need to retain or copy the data retrieved from the archive like so:
array1 = [[coder decodeObjectForKey:#"array1"] retain];

need multiple inheritance in Objective C

I want to implement a movable UIView class (view moves when you touch on it and move your finger), e.g.:
#interface MovableView : UIView {
some members;
}
-touchesBegan;
-touchesMoved;
#end
How can I apply this code to UILabel, UIButton etc without multiple inheritance? In C++, I'd do it like this (with UIView as a virtual base to MovableView):
struct MovableLabel : UILabel, MovableView {};
Categories are offered as an alternative to multiple inheritance but I definitely don't want to extent UIView using categories because that would apply to all views in my application. I only want some of my views to be movable.
My current strategy to avoid code duplication is putting MovableView code in a header file and including it everytime I need some class to use it. Besides that I have to add members of MovableView to whatever class I am writing. It is pure code ugliness but better than copy/pasting code all over my project.
Does anyone have any other ideas to get around this Objective C limitation [of not having multiple inheritance]?
Thanks,
Altan
Objective-C won't let you do this mixin inheritance thing, except by adding the same category to each of the classes you care to augment. And then, you won't get extra instance variables.
For your specific problem, I might approach it by designing MovableView as a container class that was just an object that has a child view (UILabel, UIButton, etc) that you want to move as its subview.
Categories would not help anyway because replacing the real touchesBegan method on UIButton would be pretty bad... you can't call [super] from a category.
Another solution would be to inject a method into those class definitions with something like:
Method origMethod = class_getClassMethod([UIButton class], #selector(touchesBegan));
Method newMethod = class_getClassMethod([TemplateClass class], #selector(replacementTouchesBegan));
method_exchangeImplementations(origMethod, newMethod);
But that's pretty fiddly. Although if it's really where you want to go, it's worth looking into...
What I would do is make a MovableView class that inherits from UIView, and simply add whatever you want movable as a subview of that view (which I think might be slightly different than what quixoto was saying, my apologies if not). It gets to respond to touches first and passes along whatever it doesn't need to the next responder... no need to build up a special class with a single subview. Then in IB you can just place these movable views wherever and put things inside of them.
In general compositional techniques like this are very handy across UIKit rather than modifying core classes.

[iPhone]Objective C, objects which do not conform to NSCoding. How to write them to a file

I am using an Objective c class, a subclass of NSObject.
This class cannot be modified. I have an instance of this class that I wish to write to a file which can be retrieved and later reinstate. The object does not conform to NSCoding.
To sum up, I need to save an instance of a class to a file which can be retrieved later, without using any of the NSCoding methods such as NSKeyedArchiving encodeWithCoder ...
Using them returns this...
NSInvalidArgumentException ...encodeWithCoder:]
unrecognised selector sent to instance...
Is there any other way I can store this object for later use
Thank you
p.s The class is a UIEvent
Can you subclass the class and implement the NSCoding protocol in the subclass?
Depending on what the object has as members, you could create a dictionary, and set each member's value as a key in the dictionary and then write that to file.
However, I don't understand why you can't subclass the object and implement the NSCoding protocol, as Jaanus suggested?