I've the following problem:
I've added a NSImageView to my NSWindow. I set the controller of my Window as the file's owner. I can open my window, click on buttons. It works great. But when I try to set an icon to the ImageView, nothing happens because it's null.
What could be the reason it's null? I checked, and it's connected to the IBOutlet.
Thx
Remember, Just connecting to the IBOutlet will not allocate the memory for given variable. Since you haven't allocated the memory it shows null.
So, do this first.
#property (nonatomic) IBOutlet NSImageView *imageView;
and then
#synthesize imageView = _imageView;
and then before you set the image you need to alloc and init the _imageView.
like,
_imageView = [NSImageView alloc] init];
Related
I user Interface Builder to position a UIButton and a UIImageView superimposed.
In the code, I change button label if image exists
In Example.h
#property (strong, nonatomic) IBOutlet UIButton *takePicture;
#property (strong, nonatomic) IBOutlet UIImageView *image;
In Example.m
[self.image setImage:aPhoto];
(...)
NSString *pictureButtonTitle = myCondition#"Changer la photo":#"Ajouter une photo";
(...)
[self.takePicture setTitle:pictureButtonTitle forState:UIControlStateNormal];
I use this code for a view, and I see "Change picture" correctly on my picture when myCondition is true.
But in another view, nothing appears !! WHY ?
You could either do it in the interface builder - as you did, or do it in your viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view bringSubviewToFront:self.button];
}
Arranging the outlets in the IB as you did mean that the lower object will be on top of the others... The button will be "front"/on top when it is placed below all other objects as you shown in the screenshot.
If you wish to arrange the outlets differently, you could use these methods as well:
[self.view insertSubview:self.button aboveSubview:self.imageView];
Or:
[self.view insertSubview:self.imageView belowSubview:self.button];
Well, I found out : in Interface Builder, it seems that the order you place your objects matters
If you place an UIImageView before UIButton, it works well.
I see the label
But if you do the reverse : button is under the image.
And the label is not displayed
I still don't know if it's the only way to rearrange these items ?
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.
I have MainMenu.Xib which has a status menu element. The MainMenu File Owner is mapped to AppDelegate.
I also have another Xib which is a Window and it's File Owner is mapped to a ViewController with the same name.
So what I have tried and it kind of works is I have created an action in the appDelegate and have mapped the menu item in the status menu to the action in the appDelete using the First Responder.
In the action I have put:
SubscriptionsViewController *vc = [[SubscriptionsViewController alloc] initWithNibName:#"Subscriptions" bundle:nil];
[vc view];
If I step through the code it the window shows up but then goes away. So I have two questions
1) I thought there was a way to load the xib with NSMenuItem without the need of the above code.
2) How do I keep the window from closing right away? Do I need to save the view pointer or something?
edit: format code.
1) I thought there was a way to load the xib with NSMenuItem without the need of the above code.
Since NSWindow is not inherited from NSView like in iOS (UIWindow:UIView), it makes no sense to use NSViewController to load window from a xib. Use subclass of NSObject instead.
#interface SubscriptionsViewController : NSObject
#property (assign, nonatomic) IBOutlet NSWindow *window;
#end
#implementation SubscriptionsViewController
- (id)init
{
self = [super init];
if (self) {
[NSBundle loadNibNamed:#"Subscriptions" owner:self];
}
return self;
}
#end
2) How do I keep the window from closing right away? Do I need to save the view pointer or something?
It depends on the context to retain the instance of subscriptionsViewController or not. You can use below code to display a window, where the instance of window is in nib -
self.subscriptionsViewController = [[SubscriptionsViewController alloc] init];
[self.subscriptionsViewController.window makeKeyAndOrderFront:self];
Remember if "Visible At Launch" is set in nib, then the window is visible when you instantiate subscriptionsViewController.
You may add your window into the MainMenu.xib instead of using an addition xib file and create an outlet in the AppDelegare.h as
#property (assign) IBOutlet NSWindow *window;
Then all you need to do is
window.isVisible = !window.isVisible;
in the necessary action method...
I am trying to re-write my code to allow me to use xibs to design custom UIViews for re-use throughout my projects. I have created a class called SlidesView which has a .xib of the same name. I have set it up using the following IBOutlets:
#property (nonatomic, retain) IBOutlet UIImageView *ivMain;
#property (nonatomic, retain) IBOutlet UILabel *lHeader;
#property (nonatomic, retain) IBOutlet UITextView *tvMain;
#property (nonatomic, retain) IBOutlet UIImageView *ivIcon;
I have made sure these are all properly connected within the .xib file. I also have a function which I want to use to setup all the initial data for the view:
- (void)setupUsingCenter:(CGPoint)center mainImage:(UIImage *)mainImage mainText:(NSString *)mainText header:(NSString *)header andIconImage:(UIImage *)iconImage
The 'setupUsingCenter:self ...' body looks as follows:
// First check for a main image and image icon
if (mainImage != nil)
{
// Set the image
[self.ivMain setImage:mainImage];
[self.ivMain setFrame:CGRectMake(10, 10, 10, 10)];
// If image icon set that too
if (iconImage != nil)
{
[self.ivIcon setImage:iconImage];
}
}
And finally I load this .xib using the following code:
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"SlidesView" owner:nil options:nil];
for (UIView *view in views) {
if([view isKindOfClass:[SlidesView class]])
{
SlidesView *slidesView = (SlidesView *)view;
[slidesView setupUsingCenter:self.vPlaceholder.center mainImage:slideImage mainText:slideText header:slideTitle andIconImage:slideIcon];
[slidesView setTextColours:[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f]];
[self.view addSubview:slidesView];
self.viewPopup = slidesView;
}
}
I have put in the random setFrame parameters for testing purposes but for some reason the frame doesn't change. However, the image on the ivMain does change. Can anyone explain how I can change the frame for my UI objects? Also, why does it allow me to change the image but the frame seems un-editable, doesn't this mean it must be initialised?
Any help is much appreciated,
Thanks.
Are you sure autolayout is disabled on the .xib ? It is my understanding that you cannot edit frames when autolayout is on.
If that's the case, you can either disable autolayout for that particular .xib, or you can work on constraints instead of the frame property
The use of autosizing may cause some unexpected behaviors also the autoresize subviews.
You can try something like this:
[View setAutoresizingMask:UIViewAutoresizingNone];
And:
[View setAutoresizesSubviews:NO];
Where View is the UIView that you want to apply that behavior.
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).