Objects in nib file not associating with IBOutlet objects in code - iphone

OK I'm pulling my hair on on this, it makes no sense so it must either be a bug, or some extremely stupid mistake I'm making.
Here's the situation: I have a UIViewController with a xib file. It has a UIView hooked up to the view property, a UIImageView hooked up to a property called imageView, and a UISegmentedControl hooked up to segmentControl.
Yet in my code, I'm unable to assign an image to the image view. When I NSLog the object, I'm getting the address of 0x0, so it's not associating at all. I have the bindings set up in Interface builder, I've restarted XCode and clean and built the project. Nothing is working and I've wasted a stupid amount of time trying to figure this out at this point. I've also NSLoged the segmentControl object and am getting 0x0 as well. So nothing from the XIB is hooking up with the code, but the XIB itself is loading because the objects are displayed on the screen.
Does anyone have any ideas offhand of what I could be missing? This is such a simple thing, it's incredibly frustrating that it's refusing to work all of a sudden.
Here's the code below, in this case I'm accessing the objects in the setter for projectId, but I also tried accessing the imageView from another class and it didn't work either (and yes I had a #property declaration and synthesize for imageView when I tried).
DiagramViewController.h:
#import <UIKit/UIKit.h>
#interface DiagramViewController : UIViewController
{
NSUInteger projectId;
IBOutlet UIImageView *imageView;
IBOutlet UISegmentedControl *segmentControl;
}
#property NSUInteger projectId;
#end
DiagramViewController.m:
#import "DiagramViewController.h"
#implementation DiagramViewController
#synthesize projectId;
-(void)setProjectId:(NSUInteger)projId
{
NSLog(#"imageView: %#", imageView);
NSLog(#"segmentControl: %#", segmentControl);
projectId = projId;
NSString *filePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%i_schem", projectId] ofType:#"png" inDirectory:#"project-details/images/diagrams"];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
imageView.image = [UIImage imageWithData:imageData];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
And here's an image of the Interface Builder hookups:

You need to make sure you're doing this in the -viewDidLoad method, otherwise the nib file hasn't actually unarchived the objects and thus they will be set to nil. In particular, you can't access items from the nib within an -init method.
Without seeing your code however, it's hard to say if this is the case for you.
EDIT: Ok, thanks for posting code. When does -setProjectId: get invoked? Are you sure the nib has finished loading at this point? You have no viewDidLoad method in your view controller so it appears you never check for this at any time.
See: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
viewDidLoad
Called after the controller’s view is loaded into memory.
(void)viewDidLoad
Discussion
This method is called after the view controller has loaded its associated views into memory. This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method. This method is most commonly used to perform additional initialization steps on views that are loaded from nib files.

Sounds like something went wrong when you wired up the IBOutlets.
In IB right click on the UISegmentedControl and post a screenshot so that we can see what you have there. If you see anything 'yellow' then you may have found your problem.

Related

IBOutlet UITextView is nil after calling another class initWithNibName and back to the class using method

My problem is that in ProductNote class UIButton Action I did initWithNibName Notes Class to show the popOver with a UITableView in Notes Class. I am fetching data from sqlite and then load it to UITableView in tableViewDidSelectRowAtindexPath:. I got the selectedNote and creates object of ProductNote Class to call selectedNote instance method that only set IBOutlet's textview.text but its nil. Thats my problem, Below is the code please help me to knowing why i face this type of issue. I am using Xcode 4.3.3 and not using ARC. Manually I defined dealloc method on every ViewController
//**ProductNote.h Class****************************
#import <UIKit/UIKit.h>
#class Notes;
#interface ProductNote : UIViewController<UIPopoverControllerDelegate>
{
UIPopoverController *popOverController;
UITextView *txtmesssagenotes;
Notes *objNotes;
}
#property (retain, nonatomic) IBOutlet UITextView *txtmesssagenotes; //It is Connected to ProductNote.xib
#property (retain,nonatomic) UIPopoverController *popOverController;
#property (retain,nonatomic) Notes *objNotes;
-(IBAction)presentPopOver:(UIButton*)sender;
-(void)selectedNote:(NSString*)note;
#end
//ProductNote.m Class
#implementation ProductNote
#synthesize txtmesssagenotes,popOverController,objNotes;
-(IBAction)presentPopOver:(UIButton*)sender
{
self.objNotes=[[Notes alloc]initWithNibName:#"Notes"bundle:nil];
UIPopoverController *popOver=[[[UIPopoverController alloc]initWithContentViewController:objNotes]autorelease];
self.popOverController=popOver;
self.popOverController.delegate=self;
self.popOverController.popoverContentSize=objNotes.view.frame.size;
CGRect rect=[self.view convertRect:objNotes.view.frame fromView:self.view];
rect.origin.y+=110;
rect.origin.x+=23;
[self.popOverController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:0 animated:YES];
//Then I get popOver with tableview
}
-(void)selectedNote:(NSString*)note
{
self.txtmesssagenotes.text=note;
//Here I am getting txtmesssagenotes=0X0 or nil. Using self or without self Please tell me the reason.
}
- (void)dealloc {
[txtmesssagenotes release];
[popOverController release];
[objNotes release];
[super dealloc];
}
#end
#interface Notes : UITableViewController
{
NSMutableArray *arrNotes;
NSString *selectedNote;
UITableView *tblNotes;
}
#property(nonatomic,retain)NSMutableArray *arrNotes;
#property(nonatomic,retain)NSString *selectedNote;
#property(nonatomic,retain)IBOutlet UITableView *tblNotes;
#end
//Actually I skip the other methods that makes larger program to read . other methods only used to fetch data from sqlite and fill up the arrNotes that is used to fill all rows in the tableview.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedNote=[self.arrNotes objectAtIndex:indexPath.row];
//Here self.arrNotes valid object and contains the data, and assigns NSString object to self.selectedNote
ProductNote *productNote=[[ProductNote alloc]init];
[productNote selectedNote:self.selectedNote]; //after calling selectedNote i goto to selectedNote implementation or definition above.
[productNote release];
}
-(void)dealloc
{
[arrNotes release];
[selectedNote release];
[tblNotes release];
[super dealloc];
}
It is because just as you stated that txtmesssagenotes was created using IB. And you never presented your ProductNote UIViewController instant productNote. So the txtmesssagenotes was never allocated and initialized properly.
Are you calling selectedNote: before your view has loaded?
Just because you created a view controller with a xib doesn't mean that it loads all your subviews immediately.
The xib is parsed and all your views are created the first time that the view controller's view property is asked for - this is usually just before it appears for the first time.
Until then, all the IBOutlet connections will be nil.
You need to store the text for your note as an NSString property of you view controller. Then, inside either viewDidLoad you need to call self.txtmesssagenotes.text=self.note; - viewDidLoad is automatically called just after your view is loaded so you are guaranteed to have all your IBOutlets set by then.
You shouldn't rely on your view objects to store the state of your app (in this case, the text of the note). If a low memory warning is received while your view controller isn't visible then all your view objects will be deleted - they will only be recreated when your view controller becomes visible again - if you are storing your note string inside a UITextView then it might no be there the next time you ask for it :) You should always store data in a property of your view controller (or somewhere else you control) and create your views using it.

iPhone Method Questions (dealloc an viewDidUnload)

I have been working on an app, and the book I read said to put these statements into the viewDidUnload and dealloc methods. What other information should go here? I have buttons and labels in my program. Do I need to do anything for them?
I want an efficiently running application.
Here's my code:
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.doublePicker = nil;
self.color = nil;
self.choice = nil;
[super viewDidUnload];
}
- (void)dealloc {
[doublePicker release];
[color release];
[choice release];
[super dealloc];
}
You should only release your IBOutlets and other UI elements in viewDidUnload. All the other data that you allocated in your view controller (as well as the IBOutlets) should be released in the dealloc method. That's because the view can be loaded and unloaded multiple times during the lifetime of a view controller. For example, a view can be unloaded if it is not visible, but the data behind it (in the view controller) still needs to be kept in memory. When both the view and its controller are no longer needed, the dealloc method is called.
The code you posted is correct, but you should also create properties for your outlets, like so:
in your .h file:
#property (nonatomic, retain) UIPickerView *doublePicker;
#property (nonatomic, retain) UIPickerView *color;
// ...etc
in your .m file:
#synthesize doublePicker;
#synthesize color;
// ...etc
There is some debate about this technique (explicitly retaining your outlets and releasing them like this), but it is the technique that Apple recommends:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmNibObjects.html#//apple_ref/doc/uid/TP40004998-SW2
The general rule is that you want to call release on anything that you alloc or init. Components that you create in xib's do not need to be released.

How do i modify a UILabel's properties when my MyViewController is unarchived from a nib?

I am currently creating my first app for a personal project and I'm having trouble modifying a UILabel's properties when MyViewController is loaded.
From other tutorials, I see that when the nib for my app is unarchived, the initWithCoder app is called:
#synthesize tipText; //references the UILabel i created in my nib file
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
self.tipText.layer.cornerRadius = 8; //the UILabel property i wish to modify
}
return self;
}
However, when the above code runs, the tipText is not bound to any memory yet.
Where should i place my self.tipText.layer.cornerRadius code such that tipText is initialized and before the UILabel is displayed in my UI?
You want to do this in -[UIViewController viewDidLoad]
you should implement the following method
-(void)awakeFromNib
and make changes there. This method is called on objects after they are unfrozen from a nib file.

Custom view with nib as subview doesn't seem to be loading

I've created a custom view that loads its content from a nib, like this:
/* PricingDataView.h */
#import <UIKit/UIKIt.h>
#interface PricingDataView : UIView {
UIView *contentView;
}
#property (nonatomic, retain) IBOutlet UIView *contentView;
#end
/* PricingDataView.m */
#import "PricingDataView.h"
#implementation PricingDataView
#synthesize contentView;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[[NSBundle mainBundle] loadNibNamed:#"PricingDataView" owner:self options:nil];
[contentView setFrame:frame];
[self addSubview:contentView];
}
return self;
}
/* ... */
In the nib file I set PricingDataView as the type of the File's Owner, and connected the contentView outlet in IB. I placed a regular UIView from the Interface Library onto the full-sized view shown to the user, and then changed it's class name to PricingDataView. It all builds, but at runtime, nothing is rendered where my custom view is supposed to be.
I put breakpoints in PricingDataView.initWithFrame, but they don't hit, so I know I'm missing something that would cause the view to be initialized. What I'm curious about is that in the process of loading my other views from nibs, all the initialization happens for me, but not with this one. Why?
Are you sure your other views aren't using a UIViewController? Here's a quote from the documentation for initWithFrame: from UIView:
If you use Interface Builder to design
your interface, this method is not
called when your view objects are
subsequently loaded from the nib file.
Objects in a nib file are
reconstituted and then initialized
using their initWithCoder: method,
which modifies the attributes of the
view to match the attributes stored in
the nib file. For detailed information
about how views are loaded from a nib
file, see Resource Programming Guide.
when you add files in Interface Builder at runtime it calls the
-(id)initWithCoder:(NSCoder *)aDecoder and not the initWithFrame.
just Undo the class name (PricingDataView) of the view that you add to UIView. In this case the initWithFrame: method would be called.
previously awakeFromNib was called in loop. As your customView and the View you are adding via Nib as a subView are of the same class type.

Dynamic object creation and release

I have a class that instances some other classes. It's working to control their lifespan, or supposed to ;) A root class managing others which contain events and are subviews of the root view.
I have a lot of graphics involved per view and need to clear things out before loading the next.
Any idea how to unload the current subview and how to load the next subview, while maintaining a named reference to it to use in event handling, from a "touches ended" method?
Thanks
// :)
Assuming that you want to dump the old subview and hold a handle to the new one, this is actually pretty easy. You want something like this:
#interface YourView : UIView
{
// Create an ivar in your class
UIView *_subview;
}
// Propertize it as retain to take care of most of the heavy lifting
#property(nonatomic, retain) UIView *subview;
#end
#implementation YourView
// Map the ivar to the property
#synthesize subview = _subview;
// Call this to put in a new subview
-(void) switchToNewSubview:(UIView*)newSubview
{
// Remove the old subview, set the new one, and if the new one isn't nil
// add it as a subview
[self.subview removeFromSuperview];
self.subview = newSubview;
if(newSubview)
[self addSubview:self.subview];
}
// Don't forget to nil out the subview on dealloc to release it
-(void) dealloc
{
self.subview = nil;
[super dealloc];
}
#end