0

I created an NSImageView using code:

@implementation IconView

-(void)awakeFromNib {

    [self setImageRect:NSMakeRect(36, 66, 144, 144)];
    [self setImageView:[[NSImageView alloc] initWithFrame:[self imageRect]]];
    [self addSubview:[self imageView]];

    //For the frame around the image:
    [self setFrameRect:NSMakeRect(18, 42, 180, 180)];
    [NSBezierPath setDefaultLineWidth:12];


}

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.

    [[self alertColor] set];
    [NSBezierPath strokeRect:[self frameRect]];
    [[[self imageView] image] setSize:[self imageRect].size ];
}

And I set the NSImageView's image like this:

@implementation IconViewController


-(void)setColor:(NSColor *)color forApplication:(NSRunningApplication *)app {

    IconView* iconView = (IconView*)[self view]; //The view associated with this controller
    [iconView setAlertColor:color];
    [[iconView imageView] setImage:[app icon]];
    NSLog(@"app icon: %@", [app icon]);

    [[self view] setNeedsDisplay:YES];

}

The log output is this:

2015-05-07 22:29:27.711 HelloApplication[1569:17913] app icon: <NSImage 0x608000260c00 Size={32, 32} Reps=(
    "<NSIconRefImageRep:0x608000083bb0 iconRef=0x103 size:128x128 pixels:128x128>",
    "<NSIconRefImageRep:0x608000083d90 iconRef=0x103 size:128x128 pixels:256x256>",
    "<NSIconRefImageRep:0x608000083e30 iconRef=0x103 size:256x256 pixels:256x256>",
    "<NSIconRefImageRep:0x608000083de0 iconRef=0x103 size:256x256 pixels:512x512>",
    "<NSIconRefImageRep:0x608000084150 iconRef=0x103 size:512x512 pixels:512x512>",
    "<NSIconRefImageRep:0x608000084100 iconRef=0x103 size:48x48 pixels:48x48>",
    "<NSIconRefImageRep:0x608000084790 iconRef=0x103 size:36x36 pixels:36x36>",
    "<NSIconRefImageRep:0x608000084ec0 iconRef=0x103 size:36x36 pixels:72x72>",
    "<NSIconRefImageRep:0x608000084470 iconRef=0x103 size:32x32 pixels:32x32>",
    "<NSIconRefImageRep:0x608000085000 iconRef=0x103 size:32x32 pixels:64x64>",
    "<NSIconRefImageRep:0x608000085050 iconRef=0x103 size:18x18 pixels:18x18>",
    "<NSIconRefImageRep:0x608000084fb0 iconRef=0x103 size:18x18 pixels:36x36>",
    "<NSIconRefImageRep:0x6080000850a0 iconRef=0x103 size:16x16 pixels:16x16>",
    "<NSIconRefImageRep:0x6080000850f0 iconRef=0x103 size:16x16 pixels:32x32>",
    "<NSIconRefImageRep:0x608000085140 iconRef=0x103 size:512x512 pixels:1024x1024>"
)>

So, it looks like I have an NSImage--but it won't display. I see the stroke of the frameRect in the proper color, but no image displays. IB shows that in IconView.xib my IconViewController(File's Owner) is connected to the IconView:

enter image description here

My window controller does this:

//
//  MyWindowController.m
//  HelloApplication
//

@implementation MyWindowController


- (void)windowDidLoad {
    [super windowDidLoad];

    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.

}

- (void)setUpView {

    [self setViewCtrl:[[IconViewController alloc] initWithNibName:@"IconView" bundle:nil]];
    …
    …
    NSView* view = [[self viewCtrl] view];
    [[self window] setContentSize:[view bounds].size];  //[view bounds] returns a struct, whose elements must be
    [[self window] setContentView:view];                //accessed with dot notation.

   …
}

-(id) initWithWindowNibName:(NSString *)windowNibName {

    if (self = [super initWithWindowNibName:windowNibName]) {
        [self setUpView];
    }

    return self;
}

@end

My AppDelegate does this:

//
//  AppDelegate.m
//  HelloApplication
//

#import "AppDelegate.h"
#import "MyWindowController.h"


@interface AppDelegate ()

@property(strong) MyWindowController* windowCtrl;

@end


@implementation AppDelegate


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    [self setWindowCtrl:[[MyWindowController alloc] initWithWindowNibName:@"MainWindow"]];
    [[self windowCtrl] showWindow:self];

}

If you need more files or screenshots let me know:

//
//  IconView.h
//  HelloApplication
//

#import <Cocoa/Cocoa.h>

@interface IconView : NSView

@property(copy) NSColor* alertColor;
@property(weak) NSImageView* imageView;

@end

...

//
//  IconView.m
//  HelloApplication
//

#import "IconView.h"

@interface IconView()

@property(assign) NSRect frameRect;
@property(assign) NSRect imageRect;


-(void)awakeFromNib;

@end


@implementation IconView

-(void)awakeFromNib {

    [self setImageRect:NSMakeRect(36, 66, 144, 144)];
    [self setImageView:[[NSImageView alloc] initWithFrame:[self imageRect]]];
    [self addSubview:[self imageView]];

    [self setFrameRect:NSMakeRect(18, 42, 180, 180)];
    [NSBezierPath setDefaultLineWidth:12];


}

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.

    [[self alertColor] set];
    [NSBezierPath strokeRect:[self frameRect]];
    [[[self imageView] image] setSize:[self imageRect].size ];
}

@end

...

//
//  IconViewController.h
//  HelloApplication
//

#import <Cocoa/Cocoa.h>
#import "MyActivityMonitorDelegateProtocol.h"

@interface IconViewController : NSViewController <MyActivityMonitorDelegateProtocol>


@end

...

//
//  IconViewController.m
//  HelloApplication
//

#import "IconViewController.h"
#import "IconView.h"

@interface IconViewController ()

-(void)setColor:(NSColor*)color forApplication:(NSRunningApplication*)app;

@end



@implementation IconViewController


-(void)setColor:(NSColor *)color forApplication:(NSRunningApplication *)app {

    IconView* iconView = (IconView*)[self view]; //The view associated with this controller
    [iconView setAlertColor:color];
    [[iconView imageView] setImage:[app icon]];
    NSLog(@"app icon: %@", [app icon]);

    [[self view] setNeedsDisplay:YES];

}

-(void)applicationDidLaunch:(NSRunningApplication *)app {
    [self setColor:[NSColor greenColor] forApplication:app];
}

-(void)applicationDidTerminate:(NSRunningApplication *)app {
     [self setColor:[NSColor redColor] forApplication:app];
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do view setup here.
}

@end

This is an example from an old cocoa book, p 240-241:

Cocoa Programming: A Quick Start Guide for Developers.

Response to @Joshua Nozzi's answer:

(1)

-drawRect:— Don't set properties here, least of all, something that causes further drawing in subviews (such as setting your image view's image).

I’m actually just setting the size of the ImageView’s image. The example I was following dutifully removed any code from drawRect: that only needed to be called once, but when I try setting the size of the NSImageView's image elsewhere, e.g. in the IconViewController's viewDidLoad: method, it doesn't succeed in setting the displayed image's size, so it must happen too late. Ah, it works if I set the size in the IconViewController’s setColor:forApp: method right before telling the view it needs to be redrawn:

-(void)setColor:(NSColor *)color forApplication:(NSRunningApplication *)app {

    IconView* iconView = (IconView*)[self view]; //The view associated with this controller
    NSImageView* imageView = [iconView imageView];

    [iconView setAlertColor:color];
    [imageView setImage:[app icon] ];
    //NSLog(@"app icon: %@", [app icon]);
    [ [imageView image] setSize:[iconView imageRect].size ];
    [[self view] setNeedsDisplay:YES];

}

(2)

Your call to -setupView comes during your window controller's initialization, but your [window's] nib hasn't been loaded yet...

It looks like these are the lines that concern you:

- (void)setUpView {
    …
    …
    [[self window] setContentSize:[view bounds].size];  //[view bounds] returns a struct, whose elements must be
    [[self window] setContentView:view];                //accessed with dot notation.

Calling [[self window] doSomething] before the window is unarchived means that [self window] will be nil, and calling [nil doSomething] won’t do anything. I moved those lines into the Window controller’s windowDidLoad method:

@implementation MyWindowController


- (void)windowDidLoad {
    [super windowDidLoad];

    // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.

    NSView* view = [[self viewCtrl] view];
    [[self window] setContentSize:[view bounds].size];  //[view bounds] returns a struct, whose elements must be
    [[self window] setContentView:view];                //accessed with dot notation.


}

And it seems there may be other errors as well: I’ve read that you shouldn’t call accessor methods in init methods--instead you should access the ivars directly.

(3)

You appear to have application delegate methods (did launch / did terminate) in your icon view controller

No, I don’t think that is the case; the tutorial I’m following used those confusing method names. I kept thinking they were Cocoa methods so often myself that I actually put a comment in my code indicating they were custom methods. Here is the declaration of the IconViewController:

@interface IconViewController : NSViewController <MyActivityMonitorDelegateProtocol>

And applicationDidLaunch:/applicationDidTerminate: are the methods declared by MyActivityMonitorDelegateProtocol. The application in the method names actually refers to any application that is launched/terminated on my system while my app is running.

7stud
  • 46,922
  • 14
  • 101
  • 127
  • the image should be displayed in a NSImageView as far as I can tell from your code. Thus you call setImageView ... how is that defined? What does it do? Can you check that the NSImage view exists at all? – Volker May 08 '15 at 08:03
  • 1
    @Vollker, *you call setImageView ... how is that defined?* -- It's a property in IconView.h: `@property(weak) NSImageView* imageView;` Initially, I declared that as an IBOutlet, but subsequently I implemented the NSImageView in code, so I deleted `IBOutlet` in the declaration. If I change `(weak)` to `(strong)` the image displays. I guess if the NSImageView is created in IB, a parent has a pointer to it? But when I create the NSImageView in code and assign it to a weak pointer, then as soon as that method ends, the NSImageView is deallocated because nothing retains the NSImageView. – 7stud May 08 '15 at 17:16
  • that is due to ARC and how objects in your interface when not top level are strongly hold. – Volker May 08 '15 at 17:48
  • @Volker, In a test project, I tried dragging an NSImageView onto a window, but the Connections Inspector fails to show any referencing outlets for the NSImageView. But what you are saying is that all objects in IB that are not *toplevel* objects have some code somewhere that creates a strong pointer that points to them, right? – 7stud May 08 '15 at 18:13
  • not code, but objects have a pointer which them selfs are strongly referenced and thus "kept alive": https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226 search for Nib on that page and you'll see the Apple explanation. – Volker May 08 '15 at 18:37

1 Answers1

1

Some points:

  1. Your -drawRect: implementation should be as fast and lightweight as possible, which means it should only handle drawing. Don't set properties here, least of all, something that causes further drawing in subviews (such as setting your image view's image).

  2. Your call to -setupView comes during your window controller's initialization, but your nib hasn't been loaded yet. Meanwhile your -windowDidLoadNib method sits empty, but for its comment suggestion that it's for setting things up after stuff's been loaded from nibs. This is a recipe for messages sent to nil and some / all things not working right. Try setting up your stuff from -windowDidLoadNib instead of the init routine.

  3. You appear to have application delegate methods (did launch / did terminate) in your icon view controller, yet you specify a dedicated app delegate. Application delegate methods need to be in one place: the actual delegate.

I hope this helps.

Joshua Nozzi
  • 60,946
  • 14
  • 140
  • 135