UIViewController encodeWithCoder fails when view contains a UIButton with custom image - iphone

Basically, my problem is exactly what it says in the title. When I try to encode a subclass of UIViewController, calling [super encodeWithCoder] gives an NSInvalidArgumentException. Specifically, I get -[UIImage encodeWithCoder:]: unrecognized selector sent to instance XxXXXXXX.
The only image image in the view is on a UIButton, which is also supposed to conform to NSCoding, and the stack trace includes a call to [UIBUtton encodeWithCoder]. The button is created programmatically with [UIButton buttonWithType:UIButtonTypeCustom], and the image is set with setImage: forState:. I really have no idea what is going on here. Am I missing something obvious, or does UIButton just not really conform to NSCoding?

I can't imagine why you would want to be archiving view objects within your app, but you probably need to add NSCoding support yourself by writing a category on UIImage.
For details see: iPhone - Why does the documentation say UIImageView is NSCoding compliant?
View objects support NSCoding because the view loading system uses it to load objects from Nib files. But the UINib class does some additional work that NSKeyedArchiver does not.
If you just want to store state between launches, it's better to store data and not view state. Especially if you ever plan to update your app and make any changes whatsoever to the view layout. (This is the motivation behind Model/View/Controller separation.)

Related

How does UIImage get constructed when created from an XIB?

I'm trying to do some fanciness with XIBs and that includes wanting to somehow get and store the paths of images loaded from the xib. To do this, I made some categories and did some method swizzling to override all the UIImage constructors to save their path before calling their parent constructor.
But due to Apple's black box BS with all their XIB stuff, absolutely none of the exposed constructors for UIImage seem to get called when I create a UIImageView through [UIViewController initWithNib...].
Does anybody know what function call happens or how they do this? I can't find any information whatsoever that exposes what initWithNib actually does behind the scenes.
Thanks!
EDIT:
If you're in a similar situation, you may try using the accessibilityLabel / accessibilityHint which is automatically populated with the image path. The only issue is that accessibility needs to be enabled or these values are nil.
I know the objects constructed from a Nib are being unarchived according to the NSCoding protocol; you need to override initWithCoder: in this case.
You could use swizzling to replace UIImageView's initWithCoder: method, then snoop around to see if an image name or path is available in any of the coder's keys. This might be more effective than hacking UIImage itself, since for all we know UIImageView could be using a custom subclass that you don't have access to.
the initWith... methods are meant for programatically creating UIImageView objects.
Sounds like you want to catch things as they are instantiated from XIB files. That would be the parent class UIView's [initWithCoder:] method.
As the UIView documentation says:
initWithCoder: - Implement this method if you load your view from an
Interface Builder nib file and your view requires custom
initialization.
You are both close to right - I tried doing this with UIImageView which works for initWithCoder as you guys are both suggesting. However, UIImage doesn't get initWithCoder called for some reason, instead it uses initWithCGImageStored:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation.
If and when I actually get the path out of this as I desire I'll post it up here. Thanks for the help, gents.

Showing the same UIView instance in different UIViewControllers

Is it possible to somehow store a UIView instance in such a way that it can be accessed from other view controllers? I know this is probably bordering on "globals" which they say are bad, but I digress. All I know is I have a couple UITabBar tabs that need to reference the same instance of a view that was instantiated in one tab and needs to be displayed again in another tab. What's the best approach for doing something like that?
Sure. You just need to store a retained reference to the UIView object in a persistent object. For example, you can add a retained property to your UIApplicationDelegate subclass. You can have that delegate instantiate the view, and all the controllers would just ask the app delegate for the view. If you have a root view controller that is always available, you could retain it there.
Maybe thinking through the overall structure of your app can help find the "right" place to store the UIView. Below I present an app structure I frequently use, not necessarily as advice on how you should structure your app, but as an example to expand the options you can consider to help you with thinking about the best structure for you app.
I write a lot of brochure like apps for our clients. In these apps I need to present a number of views, each somewhat analogous to pages in a brochure. Some of these views will have user interaction, and need to retain their state when off screen, or share the state data with other views.
To manage these apps I create a presentation manager object. This object will retain the overall state of the app, including views that must persist when not displayed. I use one master UIViewController that owns the presentation manager and is responsible for all common view control operations. The specific logic for individual views will go in UIView subclasses for each view. These individual views are created by the presentation manager, and can ask that manager for what it knows, including any persistent views.
You can just use dependency injection, to inject the same view instance to the view controllers like this:
UIView *myView = [UIView new];
UIViewController *controller1 = [UIViewController new];
UIViewController *controller2 = [UIViewController new];
controller1.view = myView;
controller2.view = myView;
[myView release];
B/c you use UITabBar I would suggest to add your custom view to the window in the app delegate. Then you don't have to store it, just hide it. You can use either NSNotificationCenter to send notifications to show the view or you can call your appDelegate and show the view manually.

Why not UIImage itself but UIImage view

I'm learning to develop apps for Iphone. I follow a book by Apress which I find very useful. But as nothing is perfect some issues are not well described and just skipped.In one of the applications I have to assign five images to each of the five components of a pickerview. But my question is why do/can not we use an instance of UImage itself but UIImageView to display on the picker.
As in above question you are asking why we can not use UIImage instead of UIImageView as component for UIPickerView.
UIImage is subclass of NSObject class, If we talk in terms of M-V-C its a M(model). and model in itself is nothing until its used.
UIImageView on the other hand is subclass of UIView which stands for V(view) in M-V-C so it uses your model for its contents.
So, they are two different things, not alternate. Also, if you go through UIPickerView's class documentation then you will find that it has method
- (UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component
that configures view for specified row component. so it meanse we need view(UIImageView) and not the model class(UIImage). here for component you can return subclass of UIView.
Please refer apple's documentation.
Thanks,
You need a frame to place your photo, similarly You need UIImageView as place holder to place UIImage.
UIImage is just an Image loaded from file. View is able to handle touch events and only Views are displayed.

UIImageView Created In Interface Builder Set To nil When Needed

I have an application where small number of objects are defined in the MainWindow. A pair of these objects are a UIViewController and the UIImageView that goes along with this controller. When the application starts up, the entire MainWindow.xib get loaded & I can see that this UIImageView has been loaded into memory. At this point, I have no need for this UIImageView.
When I need the UIViewController, I perform the usual alloc/init setup. At this point, when I look at this controller with the debugger, the controller is setup correctly, but the UIImageView that should have been pre-wired with Interface Builder is always nil.
Any suggegstions on how to make sure this UIImageView is loaded into memory correctly? This is the only case where I've had an issue with objects defined within this single MainWindow.xib file.
Assuming you have connected the view outlet, all you need to do is access the view property. When you look in the debugger, you're seeing the property's corresponding ivar. When you access the property in code, it should load for you. So, it should be as simple as:
[controller view];
Not quite sure I understand the problem, but instead of having the UIImageView in MainWindow.xib, why not have it in the xxxViewController.xib where it's actually going to be needed?

iphone dev: UIImageview subclass interface builder - how to call custom initializer

I messing with iphone developement. I have a uiimageview subclass that I want to use to detect touches. I have sucessfully added to interfacebuilder and I can detect a touch in my UIImageview subclass within my application so all is good on that front. however my UIImageView subclass has a custom initializer which is not called when it is created in interface builder.
if I manually initialize the UIImageview and add it programmatically I think it will work but then I lose the ability to 'see' my positioning in Interface builder.
how can I either
1) 'see' a uiimageview in interface builder that is added in code? (not possible?)
2) call my custom initializer when the subclass is instantiated in interfacebuilder.
thanks
Hi thanks for suggestions. I think I'm getting closer to understanding the relationship between the xib and the viewcontroller.
I now am sucessfully adding my UIImageView subclass programmatically and using my custom initiializer which overrides InitWithFrame.
I think I read that the xib calls 'awakeFromNib' so I could equally add my iniitialization code in there. I like the idea of adding it programmatically as I have more control (although harderto set up my IU)
so one more realted question. if I add an UIImageView subclass in interface builder. I can see it and detect touches on it. if I want to refer to it in the view controller class do I have a pointer to it? i.e. is there a variable name for it? the UIImageViews I create myself I obviuosly know what they are called.....
You likely have put your instructions in the wrong initializer.
According to the documentation, objects unarchived from a NIB are initialized with initWithCode: if they conform to the NSCoding protocol; objects that don't conform to NSCoding are initialized with init.
In this particular case, UIImageView does conform to NSCoding. It's likely that you have you intended for initWithFrame: to be called and put your instructions in that method.
Can you not simply put your initialisation logic in viewDidLoad? In particular,
-(void)viewDidLoad {
[super viewDidLoad];
// Put whatever was in your custom initialiser here.
}