I've never used NSCoding before and I'm very confused about how it should be implemented.
My current iPad app has a UIImageView (called "background") which is a property of my main view controller. "background" has a UIImage "image" property (obviously) and various subviews which are added by the user. The added subviews are my own custom subclasses of UIImageView.
I need to be able to save the state of the "background" UIImageView so it can be restored with the same image and all the subviews in place as it was when archived.
I understand UIImageView conforms to the NSCoding protocol, but I'm not sure where to implement encodeWithCoder and initWithCoder. Do I call these from my main view controller? Do I need to create a category for UIImageView which allows me to override these methods?
Do I need to write code for archiving every property of my "background" UIImageView and its subviews? I have read elsewhere on SO that UIImage does not conform to NSCoding so needs to be subclassed or have a category added in order to be able to archive UIImageView.
I thought there would be a simple way to save to disk an object including all its properties, subviews etc. It seems there's a lot that needs to be done in order for me to save this "background" UIImageView and restore it later. I'm struggling to visualise everything I need to do. Any pointers much appreciated!
Serialization (aka archiving and unarchiving) is actually pretty complicated, but the degree to which Cocoa makes it easy is a pretty impressive feat.
Once you've set things up so that the UIImageView and all of its properties that you want to keep conform to NSCoding, then all you have to do to save the object is:
NSData *dataToSave = [NSKeyedArchiver archivedDataWithRootObject:yourImageView];
And then store that NSData somewhere. Then, to unarchive the object,
UIImageView *restoredImageView = [NSKeyedUnarchiver unarchiveObjectWithData:dataToRestore];
after recovering the NSData from somewhere.
As for making everything conform to NSCoding, UIImageView conforms to NSCoding, as does UIView, so between your UIImageView, its subviews, and their properties, everything probably conforms to NSCoding except for the actual UIImage. For that, if you google you can find lots of categories people have made to make it conform to NSCoding, so just include one of them in your project and you should be fine.
Related
Across my app I have several different subclasses of UIView: UIDatePicker, UIPicker, UIButton, UITableView, UITableViewCell, UITextView, etc. etc... For each of these I'd like to add a very simple drawRect custom implementation that I have working great.
Is there a simple way to get multiple subclasses of UIView to all have the same drawRect implementation without creating a subclass and repeating the same code across each UIPicker, UIButton, etc. etc... ?
I realize the solution to this is probably to write a delegate class for UIView's layer property and do the custom drawing in drawLayer, but I thought I would ask before I go re-working my code.
The answer seems to be no.
Furthermore, it appears that my idea for the workaround also doesn't work: I was thinking I could write a nice little class that implements the drawLayer method from CALayerDelegate and do the drawing in there, and then in each UIView or UIView subclass' init method do a self.layer.delegate = niceLittleCALayerDelegateClass.
My research, however, happened upon this: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CALayer_class/#//apple_ref/occ/instp/CALayer/delegate which contains the damning sentence: In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer.
So, double nope.
Against rmaddy's advice I'm going to just write a subclass for each of the UIView subclasses I want to implement this drawing behavior in. We'll see how that goes.
Update:
I can't believe I didn't think of this before, but the "right" way to do this (that is to say without subclassing UIView's subclasses and adding a custom drawRect method to each subclass's subclass), from everything I can find, seems to be to either create a subview or a sublayer with a transparent background that does whatever custom drawing you want.
Obviously this is going to draw on top of the UIView you're actually using, so this would get exceedingly complicated if you're trying to draw things that interact with the default elements of the UIView, but for my purposes (just a simple frame drawn with a UIBezierPath) it seems to work great.
At present I'm not sure if the subview or the sublayer approach is more efficient. If anyone can shed light on that, I'd appreciate it.
I'm trying to refactor my code in the best possible way and I'm wondering what the proper architecture for the given situation is.
What I'm Trying To Do
What I'm doing is pretty simple: I have some custom CALayer subclasses that represent an interactive UI element. They are broken up into multiple layers since some of the parts of the UI are static, so I didn't want to redraw those static elements needlessly. Right now, the layers are added as sublayers in the initialization part of a CustomView class that is a subclass of UIView.
There is currently no corresponding CustomViewController class that is a subclass of UIViewController because when I'm using the CustomView, it's contained within a UITableViewCell or a part of a generic UIViewController with other views in it, so I felt another UIViewController for each CustomView instance would be redundant.
Also of importance is the that the only operation that I'm doing inside of the UIView class is I'm responding to touch events and sending the touch information to the sublayers so that the UI can update its appearance accordingly. I'm not overriding the drawRect method or anything like that.
The Question
Basically, I'm trying to figure out whether I should either:
Option 1:
Get rid of the CustomView class, create a CustomViewController class that is a subclass of UIViewController, and simply add the CALayer objects as sublayers of the CustomViewController's built-in view property.
or
Option 2:
My thinking about the UIViewController subclass being redundant is correct, so I should leave it the way I have it and have a CustomView class with the CALayer objects inside of it.
I would highly appreciate any advice on this.
I think that in terms of MVC, the code you're describing (option #2) is well written and maintains a very clear boundary of responsibility. You're not writing any code that has nothing to do with the view layer itself in this class which is great. I think that in this case there's no need for a separate UIViewController subclass to manage these instances because as you said - they are handling their own touch events and visible layers (exactly their responsibility).
If for any reason there is a need for something more complex that requires data related logic or other such computation, I would definitely consider subclassing a UIViewController or maybe looking at the problem in an entirely different way.
Given the situation you've presented, I think that maintaining the CALayer instance within this UIView subclass ('CustomView') is the right way to go.
In my application I want to associate NSMutableDictionary to all UIElements (UIButton, UILable, etc), I can easily achieve this by subclassing each element, but I just want to know if I can make my own UIControl subclass -with a property of type NSMutableDictionary-, as superclass of all programmatically created UIElement s in anyway, so that I can reduce the number of subclasses.
Here like this
#interface UIControl : MyControls
{
}
#property(nonatomic,retain) NSMutableDictionary *details;
#end
and make MyControls as superclass of all programmatically created UIElements
You can do this, not by subclassing, but by adding your own properties and methods to UIControl itself (the superclass of UIButton, UILabel, etc.). These will then be inherited by any standard buttons, labels, etc. that you instantiate. Objective-C lets you add your own methods very easily using Categories. However, you can't add instance variables via categories. To do that, you need to use Associative References which are documented in the Objective-C Runtime Reference.
There's a good tutorial on how to do this here.
By the way, I don't necessarily disagree with inturbidus. But if you're sure you want to go this route, that's how you'd do it.
If I assign an image to a UIImage view in a xib, is that image cached so that if I access the image using UIImage imageNamed: I am getting cached Image data?
I'm using iOS 5.1
UIImage imageNamed: does its own cacheing of any images you use. The first time you use it for a given image, it'll populate the cache, and subsequently it'll use the cached version.
UIImageView in Interface Builder takes a string to tell it what image to use. It appears that the object that is actually encoded in the Xib to represent the image is a private class called UIImageNibPlaceholder, which contains a private NSString variable called runtimeResourceName. It's this class that implements the initWithCoder: method which is used when the system is loading objects from a xib.
So, the question is, inside UIImageNibPlaceholder's initWithCoder:, does it use the imageNamed: function of UIImage? I think it's reasonable to assume that it does, since the thing stored in the xib is the string runtimeResourceName, and the system is turning that string into an actual image when loading the xib.
This post on the Apple developer forums seems to clarify the point (under NDA so I can't copy it here). I couldn't find any publicly accessible information on the subject.
This may seem like a weird question but thought I would ask here before I spend a couple hours trying to implement it.
In my app, I am saving view properties (number of sub views etc) to an sqlite3 database.
While reading the data back, I though 'Would it not be so much easier to just store the view in the database?'
So, using something like a blob type do you think it would be possible?
Something like:
Bind blob to insert
view = sqlite3_column_blob(statement, 0); to retrieve
Does this sound like it could work? My biggest concern is knowing the size of the view on insert and select. Is something like this possible or is it not documented because it is too ridiculous?
Thanks!
and then getting it out
UIView conforms to NSCoding, which means you can serialize and deserialize it. This is exactly how nib files work. First, see the Archives and Serializations Programming Guide.
One easy way to serialize a UIView is like this:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:view]
You can then write out data in whatever way is convenient.
To deserialize the view, use this:
UIView *view = [NSKeyedUnarchiver unarchiveObjectWithData:data];
This will make a new copy of the entire view hierarchy.
Note that UIView only serializes its own properties. If you subclass UIView and want to serialize additional properties, you will need to override encodeWithCoder: and initWithCoder: to add your properties. This is detailed in Encoding and Decoding Objects.