2
votes

I created a Subclass a of NSView to create a custom UI including a NSTextView. Therefore I use the drawRect Method to draw my UI. I also created a Subclass of NSImageView to get a mousedown Event. Whenever I click into my Subclassed NSImageView the drawRect Method of my View gets called in an infinite Loop and I can't figure out why. Building custom UIs is new for me so maybe I'm doing something essentially wrong?

TWIComposeFrameView.m:

#import "TWIComposeWindowFrameView.h"
#import "TWIImageView.h"

@implementation TWIComposeWindowFrameView

#pragma mark draw Rect of the View

//
// drawRect:
//
// Draws the frame of the window.
//
- (void)drawRect:(NSRect)rect
{
    //// Color Declarations
    NSColor* mainClrWhite = [NSColor colorWithCalibratedRed: 0.954 green: 0.954 blue:     0.954 alpha: 1];
    NSColor* transpGrey = [NSColor colorWithCalibratedRed: 0.224 green: 0.224 blue: 0.224 alpha: 0.745];
    NSColor* composeClrGrey = [NSColor colorWithCalibratedRed: 0.858 green: 0.842 blue: 0.842 alpha: 1];

    //// Abstracted Attributes
    NSRect composeRectRect = NSMakeRect(20, 70, 475, 210);
    NSRect attachmentRectRect = NSMakeRect(499, 195, 85, 85);


    //// transpRect Drawing
    NSBezierPath* transpRectPath = [NSBezierPath bezierPathWithRoundedRect: NSMakeRect(1, 0, 599, 299) xRadius: 10 yRadius: 10];
    [transpGrey setFill];
    [transpRectPath fill];


    //// mainRect Drawing
    NSBezierPath* mainRectPath = [NSBezierPath bezierPathWithRoundedRect: NSMakeRect(10, 10, 580, 280) xRadius: 10 yRadius: 10];
    [mainClrWhite setFill];
    [mainRectPath fill];


    //// composeRect Drawing
    NSBezierPath* composeRectPath = [NSBezierPath bezierPathWithRoundedRect: composeRectRect xRadius: 10 yRadius: 10];
    [composeClrGrey setFill];
    [composeRectPath fill];


    //// attachmentRect Drawing
    NSBezierPath* attachmentRectPath = [NSBezierPath bezierPathWithRoundedRect: attachmentRectRect xRadius: 10 yRadius: 10];
    [mainClrWhite setFill];
    [attachmentRectPath fill];
    [composeClrGrey setStroke];
    [attachmentRectPath setLineWidth: 1];
    [attachmentRectPath stroke];
}

- (id)initWithFrame:(NSRect)rect
{
    if (![super initWithFrame:rect])
        return nil;

    NSColor* composeClrGrey = [NSColor colorWithCalibratedRed: 0.858 green: 0.842 blue: 0.842 alpha: 1];
    NSRect composeRectRect = NSMakeRect(20, 70, 475, 210);
    NSRect attachmentRectRect = NSMakeRect(499, 195, 85, 85);
    //// Add Controls
    [self addComposeTextView:composeClrGrey composeRectRect:composeRectRect];
    [self addAttachmentImageView:attachmentRectRect];
    return self;
}

#pragma mark -
#pragma mark Add Controls to View

//
// Create and Add the Compose TextView
//
- (void)addComposeTextView:(NSColor *)composeClrGrey composeRectRect:(NSRect)composeRectRect
{
    NSTextView *composeTextView;
    if (composeTextView == nil)
    {
        composeTextView = [[NSTextView alloc] initWithFrame:composeRectRect];
        [composeTextView setBackgroundColor:composeClrGrey];
        [composeTextView setFont:[NSFont fontWithName:@"Helvetica" size:18]];
        [composeTextView setRichText:NO];
        [self addSubview:composeTextView];
    }
}

//
// Create and Add the Attachemant ImageView
//
- (void)addAttachmentImageView:(NSRect)attachmentRectRect
{
    NSImage *defaultImage = [NSImage imageNamed:@"168-upload-photo-2"];
    NSRect ivRect = NSMakeRect(attachmentRectRect.origin.x + 5, attachmentRectRect.origin.y + 5, attachmentRectRect.size.width - 10, attachmentRectRect.size.height - 10);
    TWIImageView *imageView = [[TWIImageView alloc] initWithFrame:ivRect];
    imageView.delegate = self;
    [imageView setEditable:YES];
    [self addSubview:imageView];
    [imageView setImage:defaultImage];
}

#pragma mark -
#pragma mark TWIImageView Delegate

//
// mouseDown Event on TWIImageView
//
- (void)myImageView:(TWIImageView *)view mouseDown:(NSEvent *)event
{
    NSLog(@"imageView clicked");
}
#pragma mark - 

@end

TWIImageView.h

@class TWIImageView;
@protocol TWIImageViewDelegate <NSObject>

- (void)myImageView:(TWIImageView *)view mouseDown:(NSEvent *)event;

@end

@interface TWIImageView : NSImageView
{

}

@property (assign) id<TWIImageViewDelegate> delegate;

@end

TWIImageView.m:

#import "TWIImageView.h"

@implementation TWIImageView

@synthesize delegate = _delegate;

- (void)mouseDown:(NSEvent *)event {
    if([self.delegate respondsToSelector:@selector(myImageView:mouseDown:)]) {
        [self.delegate myImageView:self mouseDown:event];
    }
}

@end

Is it wrong to create the controls in the overwritten initWithFrame Method? Or is my TWIImageView missing something important

StackTrace:

Thread 1, Queue : com.apple.main-thread
#0  0x0000000100001cd0 in -[TWIComposeWindowFrameView drawRect:] at     /Users/thomaswickl/Documents/Development/Git     Projects/QuickQuick/QuickQuick/TWIComposeWindowFrameView.m:24
#1  0x00007fff92efa170 in -[NSView _drawRect:clip:] ()
#2  0x00007fff92ef6fe3 in -[NSView  _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView: ] ()
#3  0x00007fff92ef7a74 in -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] () 
#4  0x00007fff93045fdb in -[NSNextStepFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] ()
#5  0x00007fff92ef1d9d in -[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:] ()
#6  0x00007fff92ebbcc3 in -[NSView displayIfNeeded] ()
#7  0x00007fff93045e94 in -[NSNextStepFrame displayIfNeeded] ()
#8  0x00007fff92ebb1fc in _handleWindowNeedsDisplayOrLayoutOrUpdateConstraints ()
#9  0x00007fff934868f1 in __83-[NSWindow _postWindowNeedsDisplayOrLayoutOrUpdateConstraintsUnlessPostingDisabled]_block_invoke_01208 ()
#10 0x00007fff92b39417 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#11 0x00007fff92b39381 in __CFRunLoopDoObservers ()
#12 0x00007fff92b147b8 in __CFRunLoopRun ()
#13 0x00007fff92b140e2 in CFRunLoopRunSpecific ()
#14 0x00007fff90ce8eb4 in RunCurrentEventLoopInMode ()
#15 0x00007fff90ce8b94 in ReceiveNextEventCommon ()
#16 0x00007fff90ce8ae3 in BlockUntilNextEventMatchingListInMode ()
#17 0x00007fff92eb8563 in _DPSNextEvent ()
#18 0x00007fff92eb7e22 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#19 0x00007fff92eaf1d3 in -[NSApplication run] ()
#20 0x00007fff92e53c06 in NSApplicationMain ()
#21 0x0000000100001242 in main at /Users/thomaswickl/Documents/Development/Git Projects/QuickQuick/QuickQuick/main.m:13
#22 0x00007fff955db7e1 in start ()
2
Frankly I do not see the loop. If there is one can't you just set a break point at the beginning of drawRect: and continue a couple of times and then look at the call stack which of the methods called actually invokes drawRect again and again?Hermann Klecker
You might want to consider saving and restoring your graphics context during the -drawRect:. I don't know that it will solve your looping issue, but it will probably solve some problems down the road.gaige
i can't see a looping issue in this code .I think its happening from somewhere else...just see if you are calling setNeedDisplay always (in a loop) !!!arun.s
How I reproduce this is typing in the TxtView and then clicking on the ImageView. After a few clicks on the ImageView the drawRct Method gets called in a loop. I added the StackTrace but still don't get it.user99070
It looks like saving and restoring the Graphics context did the Job! Thank you!user99070

2 Answers

0
votes

I do not think that this answer will fix your issue. However, an init method may return a different object than self. So you better use this:

if (!(self = [super initWithFrame:rect]))
    return nil;
0
votes

Add

[super drawRect:rect];

in drawRect method