Simple problem, I have defined a UIImageView, called bigImageView in a UIViewController using the storyboard,
It's declared in the h file of that UIViewController as follows:
#property (retain, nonatomic) IBOutlet UIImageView *bigImageView;
on my appDelegate I init the UIViewController as follows:
imageViewController = [storyboard instantiateViewControllerWithIdentifier:#"chosenImageController"];
this calls initWithCoder on my UIViewController m file:
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// Custom initialization
}
return self;
}
This function is only called once so there's no double init.
However, later, when I check my bigImageView pointer, it's still nil.
Isn't the init supposed to allocate memory to it?
I think that's why when I try to set this UIImageview to hold a UIImage it doesn't display the image
Thanks
It's all working how it's meant to. First every object in the nib/storyboard gets alloc/init called on them, then all the connections are made, and then viewDidLoad is called.
You need to wait for - (void)viewDidLoad to be called on your controller, and then bigImageView should be set. If it's not set then you did something wrong in the storyboard.
init methods are not responsible for allocating any memory. All memory is allocated by the alloc method which is always called before init. Alloc will fill all your instance variables with nil/NULL/0 values, and then init gives the chance to assign initial values to each one (based on the contents of the NSCoder object usually, but it's up to you to decide what should be done).
For IB outlets however, those are setup by the nib loading process after init.
EDIT:
// ViewControllerA.m:
imageViewController = [storyboard instantiateViewControllerWithIdentifier:#"chosenImageController"];
imageViewController.image = imageToShow;
// ViewControllerB.h
#property (retain) NSImage *image;
#property (retain, nonatomic) IBOutlet UIImageView *bigImageView;
// ViewControllerB.m
- (void)viewDidLoad
{
self.bigImageView.image = self.image;
[super viewDidLoad];
}
You don't need to define initWithCoder, since you have no custom logic in there. I would delete that boilerplate code.
Here is what I would check:
In the storyboard, ensure that the class of the view controller is set properly.
Ensure that the outlet is hooked up properly in the storyboard by looking for a circle near your #property. It should be a filled in circle, not an outline of a circle.
Make sure you are reading the value only after viewDidLoad is called. Apple's only guarantee is that the outlet is set after this method call.
Update: It sounds like you want to access the image view before the view is loaded. There is no way to do this. One hack is to call viewController.view which will force the view to load, but there are many reasons why you should not do this.
A better approach would be to implement properties on your view controller which work for both when the view is not loaded and when the view is loaded. You can see an example of an elegant solution in this question. Notice how if the view is loaded, the photographerLabel will get set via the didSet method. On the other hand, if the view is not loaded, it will get set via the viewDidLoad method. For an Objective-C version of that code or for more details, see the linked video in that question.
Related
Hy guys, I'm new at ObjC and I'm still learning;
[sliderContrast setHidden:YES] (i also used slider.hidden = YES) doesn't make the slider invisible, instead it works fine with textfields. Do you know why?
I've also tried using property and synthesize but the result doesn't change
---Interface
#interface Controller : NSWindowController{
IBOutlet NSTextField *labelContrast;
IBOutlet NSTextField *valueContrast;
IBOutlet NSSlider *sliderContrast;
}
- (IBAction)changeContrast:(id)sender;
#end
---Implementation
#import "Controller.h"
#interface Controller ()
#end
#implementation Controller
- (void)windowDidLoad {
[super windowDidLoad];
[labelContrast setHidden:YES];
[valueContrast setHidden:YES];
[sliderContrast setHidden:YES];
}
- (IBAction)changeContrast:(id)sender {
}
#end
If you declare pointers for your objects but you don't allocate them yourself you can not set anything that is not there. Your setHidden: method calls end up in local void aka nowhere.
programmatically
If you go the coding way you would declare, allocate and initiate first. With
labelContrast = [NSTextField alloc] initWithFrame:NSMakeRect(x,y,w,h)];
before you call other methods of the object class (Or similar init methods).After that you can call methods on the object.
Almost all objects inherit an according -(instancetype)init or -(instancetype)initWith... method you can use. If there is no init method given, then there is another way to do it right and the moment to start reading again :).
With Interface Builder
By typing IBOutlet or IBAction in front of a declaration you just give a hint for Xcodes Interface Builder where to hook up and apply onto (associate) the placed object in (nib,xib,storyboard) with its object ID in the XML scheme to a reference in code.
So after you connected your code and the object in IB you can avoid allocation and init process for that particular object. Be aware that calling methods on objects that are not instanced yet is not working. Which is why you code in - (void)windowDidLoad, -(void)viewDidLoad or -(void)awakeFromNib because those are the methods that get called after "IB" has done its job for you.
I have an xib for the main window, and I created a custom view in the following steps:
Create a new class which inherits from NSView.
MyView.h:
#import <Cocoa/Cocoa.h>
IB_DESIGNABLE
#interface MyView : NSTableCellView
#end
MyView.m:
#import "MyView.h"
#implementation MyView
- (void)awakeFromNib {
NSLog(#"Post view awaking from nib.");
}
#end
Create a new xib, and set the root view's class to the class created above. And design in that xib.
Set outlets from the xib to the class.
And I tried to use this custom view in the main window in the following steps:
Drag a custom view to the main window's xib.
Set the class of that custom view to the class created above.
But nothing renders. From the log, I can see that code in awakeFromNib from the custom view class is executed. When I set the class to be IB_DESIGNABLE, the view gets empty in the main window's xib, different from what I designed.
I tried to set the file owner of the custom view's xib to the custom class, but nothing changed.
I guess the problem is that, the custom view's xib file is not actually loaded. When I googled it, there seem to be few references on this exact topic. So, how should I actually achieve this goal? I.e., design a view in IB, implement its methods in a class, associate these two, and expose it just like a system view for use in other xibs?
UPDATE:
I found a tutorial and realized what I lack (for correctly rendering the view when built). I have to add an outlet from the view in the xib to the view class:
#property (nonatomic, strong) IBOutlet NSView *view;
, and then load it in the (id)initWithCoder:(NSCoder *)coder method.
[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self topLevelObjects:nil];
[self addSubview:self.view];
But the view still won't render in the interface builder.
Your guess is correct: the xib is not being loaded. The nib loader doesn't know about your custom view's nib. The nib framework doesn't provide a facility for defining that connection, so you need to write code to load the xib.
Here's what I'd do. Add a contentView property to your custom view:
#interface MyView ()
#property (nonatomic, strong, readwrite) IBOutlet NSView *contentView;
#end
In your custom view's nib, set the custom class of the root view back to NSView and disconnect all the (no-longer-valid) outlet connections from it. Set the custom class of File's Owner to your custom class name (e.g. MyView). Connect the root view to File's Owner's contentView outlet, and connect all the other outlets from File's Owner to the appropriate objects in the nib.
Then implement awakeFromNib in your custom view subclass to load the nib and add the content view as a subview:
#implementation MyView {
BOOL hasLoadedOwnNib: 1;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self loadOwnNibIfNeeded];
}
- (void)loadOwnNibIfNeeded {
if (hasLoadedOwnNib) {
return;
}
hasLoadedOwnNib = YES;
[[NSBundle bundleForClass:self.class] loadNibNamed:NSStringFromClass(self.class) owner:self topLevelObjects:nil];
self.contentView.frame = self.bounds;
self.contentView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[self addSubview:self.contentView];
}
#end
Note that you have to be careful not to allow infinite recursion. When your app loads the main window's nib, it will create an instance of MyView and (eventually) send it awakeFromNib. Then, in awakeFromNib, MyView loads its own nib, where it is the File's Owner. The nib loader sends awakeFromNib to File's Owner, and this will happen while you're already in -[MyView awakeFromNib]. If you don't check for this, you get a stack overflow due to unbounded recursion.
You aren't providing any code, but here are some sanity checks:
Are you specifying the nib name correctly? In iOS its caps sensitive, but I don't think it is for you.
Check the package, is the nib actually there? Make sure it is part of the target you are building.
Could also be a frame issue. Make sure your auto-resizing parameters are set up correctly and that everything is in frame.
Another check you can do is set your IBOutlets to the actual frame of a UIView (or other) that you are interested in. Then in awakeFromNib, you can make sure their frame exists, or that they exist at all.
For reference, I'm trying to learn Objective-C through the Stanford iTunes-U course. I wanted to update one property from the setter of another (they are inherently connected. Also, is that bad style?). The property I am trying to update is a UILabel, but it doesn't work like I thought.
This code is in one of my view controllers:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[segue.destinationViewController setProgram:self.brain.program];
}
Naturally, this code calls the setter for the Program property of the incoming viewController. Here's that setter code:
-(void)setProgram:(id)program {
_program = program;
self.title = [CalculatorBrain descriptionOfProgram:program];
[self.graphview setNeedsDisplay];
self.testLabel.text = #"Trying to update your text!";
}
and the header file:
#import <UIKit/UIKit.h>
#interface GraphViewController : UIViewController
#property (nonatomic, strong) id program;
#property (weak, nonatomic) IBOutlet UILabel *testLabel;
#end
Now, when this code is run, as the segue happens, the new view DOES have its title changed (the self.title line works). However, the UILabel DOES NOT get updated. Furthermore, if I call the setter again say in viewDidLoad, it does change the UILabel. Is this because of self being updated? What's going on here?
In one of the lectures, the professor explains that outlets aren't set yet in prepareForSegue. This means testLabel is nil, and you're sending a message to nil (which is allowed and doesn't crash your app). To solve your problem, set the textLabel's text in viewDidLoad or viewWillAppear.
I believe this is "fixed" in iOS 6, but it won't be backwards compatible, so if you want to support iOS 5 still, you'll have to use viewDidLoad or viewWillAppear to update outlets (which I think is better to do anyway).
Here's my situation: I have a UIViewController that manages a hierarchy of subviews, perhaps as shown below:
This view is built from a .xib. I would like to be able to maintain access to each subview of topView – that is, I want a pointer to each so that I can, for example, say something like:
[button1 setText:#"Hello!"];
Usually, to do this, I wire up each element to which I would like access using Interface Builder, resulting in a header that looks something like this:
#interface MyViewController : UIViewController
{
__weak IBOutlet UIView *view;
__weak IBOutlet UILabel *label;
__weak IBOutlet UIButton *button1;
__weak IBOutlet UIButton *button2;
}
#end
These instance variables are __weak, which is fine, since by the time my view controller "gets" them, they're already owned by my view controller's root view (which, confusingly, I labeled "topView" in my quick diagram). In fact, I want these references to be weak – when my root view is released, so too should all its subviews be released. Great.
But let's say I want to create a new element of the UI, maybe a custom button, entirely in code. I'll call this element CustomViewClass, which will be subclassed from UIView. The instance of CustomViewClass that I will create will be called customButton. As with the other subviews of my view, I would like "access" to customButton so that I can interact with it. I know, however, that like any other subview, customButton will be owned by its superview, and that's how it should be – again, I want it to be released whenever my view is released. This makes me think that I should declare this view as a __weak instance variable or property of my view controller. Let's do that:
#interface MyViewController : UIViewController
{
__weak IBOutlet UIView *view;
__weak IBOutlet UILabel *label;
__weak IBOutlet UIButton *button1;
__weak IBOutlet UIButton *button2;
__weak CustomViewClass *customButton;
}
#end
Then, in my implementation:
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
customButton = [[CustomViewClass alloc] init];
[[self view] addSubview:customButton];
}
#end
As you've probably already realized, this won't work, and the compiler will throw a warning to boot. Something like:
Assigning retained object to weak variable; object
will be released after assignment
I currently dodge this sort of warning with some very poor style:
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CustomViewClass *customButtonLocal = [[CustomViewClass alloc] init];
[[self view] addSubview:customButtonLocal];
customButton = customButtonLocal;
}
#end
That way, I get what I want:
An instance of CustomViewClass on the screen...
...with exactly one owner, its superview...
...and no lingering variables (customButtonLocal is released immediately after the block ends).
But this can't be the "right" way to do this. So, finally, my question:
How should I be allocating and instantiating this programmatically-created __weak variable without using this middle-man workaround?
Make CustomViewClass *customButton a strong reference.
The reason why you usually declare variables for your subviews as __weak IBOutlet is that the existence of these links does not imply ownership. Subviews are owned by the object instantiated from a NIB/Storyboard. You own that object directly, and you also own its dependent objects indirectly.
The customButton is a different story: you create it programmatically, so your NIB/Storyboard does not own it. Therefore, you should make the reference to it __strong (which is the default when there are no ARC modifiers).
I know this is very basic but I need clarification. I'm trying to develop an iPad app but having trouble. My explanation may be a little too much information needed but bear with me.
I have two ViewControllers. One is called NewGameViewController while the other is called GameViewController. In the NewGameViewController I display the GameViewController like so:
GameViewController *controller = [[GameViewController alloc] initWithNibName:#"GameViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
So this creates an instance of GameViewController in memory. GameViewController has some ivars and methods defined in it. One of the ivars is an array that gets filed with seat objects when GameViewController loads (in the viewWillAppear method).
I also have an object called player. This is just a subclass of UIView. When GameViewController loads it adds up to six player objects to the subview. I need each player object to be able to access the same instance in memory of GameViewController that was loaded by NewGameViewController. Would I use a pointer in my player objects to access the same instance of GameViewController? I've found that if I load a new instance of GameViewController into memory and attempt to use it the array ivar tends to be empty and therefore unuseable. How would I be sure that my pointer to GameViewController is pointing to the correct instance of GameViewController?
When GameViewController loads it adds up to six player objects to the
subview.
So, here's what you can do. Define a property on Player called containingViewController or gameViewController.
#property (nonatomic, retain) GameViewController *containingViewController;
Don't forget to also #synthesize containingViewController; in your implementation file (Player.m) and [containingViewController release]; in your dealloc.
Then, when you initialize the players in the viewWillAppear method, set the property:
player1.containingViewController = self;
That should take care of all of your issues.