2

I have a class called MNTRectangle which is a subclass of UIImage. I have overridden the drawRect method of this class to draw a border on the image (using its frame). I have it so when the user starts a panning/dragging gesture on a class called DocumentView (subclass of UIView) it adds an instance of MNTRectangle as a subview to the instance of DocumentView. As the user continues to drag the MNTRectangle is resized. The problem is that the MNTRectangle is appearing as solid black in the end, I have tried clearing the graphics context as well as saving the context before drawing the border and restoring the context after drawing the border. No matter what I seem to try I cannot get the MNTRectangle to clear and just show the border on resize.

Here is the code for my MNTRectangle class:

@implementation MNTRectangle

- (id)init
{
    self = [super init];

    if (self)
    {
        [self setup];
    }

    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];

    if (self)
    {
        [self setup];
    }

    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (self)
    {
        [self setup];
    }

    return self;
}

- (void)setup
{
    [self setBackgroundColor:[UIColor clearColor]];
}

- (void)drawRect:(CGRect)rect
{
    // Get graphics context
    CGContextRef context = UIGraphicsGetCurrentContext();

//    CGContextSaveGState(context);

//    CGContextClearRect(context, rect);

    // Draw border
    CGContextSetLineWidth(context, 4.0);
    [[UIColor blackColor] setStroke];
    CGContextStrokeRect(context, rect);

//    CGContextRestoreGState(context);
}

Here is the code in DocumentView for the panning/dragging handling on the UIView:

-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (self)
    {
         _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    }

    return self;
}

- (void)handlePan:(UIPanGestureRecognizer *)sender
{
    if ([sender state] == UIGestureRecognizerStateBegan)
    {
        _startPanPoint = [sender locationInView:self];

        MNTRectangle *rectangle = [[MNTRectangle alloc] initWithFrame:CGRectMake(_startPanPoint.x, _startPanPoint.y, 1, 1)];
        [_objects addObject:rectangle];
        _currentPanObject = rectangle;
        [self addSubview:rectangle];
    }
    else if ([sender state] == UIGestureRecognizerStateChanged)
    {
        CGPoint endPanPoint = [sender locationInView:self];

        float height = fabsf(endPanPoint.y - _startPanPoint.y);
        float width = fabsf(endPanPoint.x - _startPanPoint.x);
        float x = MIN(_startPanPoint.x, endPanPoint.x);
        float y = MIN(_startPanPoint.y, endPanPoint.y);

        MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
        [rectangle setFrame:CGRectMake(x, y, width, height)];
    }
    else if ([sender state] == UIGestureRecognizerStateEnded)
    {
        CGPoint endPanPoint = [sender locationInView:self];

        float height = fabsf(endPanPoint.y - _startPanPoint.y);
        float width = fabsf(endPanPoint.x - _startPanPoint.x);
        float x = MIN(_startPanPoint.x, endPanPoint.x);
        float y = MIN(_startPanPoint.y, endPanPoint.y);

        MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
        [rectangle setFrame:CGRectMake(x, y, width, height)];
    }
}

Any help with this would be greatly appreciated.

  • Try setting the `opaque` property of your subclass to `NO`. It is `YES` by default, which causes the drawing and compositing system to make some assumptions. – warrenm Dec 07 '12 at 22:45
  • I have tried that as well as setting the `clearsContextBeforeDrawing` property to `YES` without any luck. –  Dec 07 '12 at 22:59

3 Answers3

2

I finally figured it out. In my handlePan: method after I set the frame of the rectangle I was missing [rectangle setNeedsDisplay];.

Now my DocumentView code looks like this:

-(id)initWithFrame:(CGRect)frame
{
   self = [super initWithFrame:frame];

    if (self)
    {
         _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    }

    return self;
}

- (void)handlePan:(UIPanGestureRecognizer *)sender
{
   if ([sender state] == UIGestureRecognizerStateBegan)
   {
        _startPanPoint = [sender locationInView:self];

        MNTRectangle *rectangle = [[MNTRectangle alloc] initWithFrame:CGRectMake(_startPanPoint.x, _startPanPoint.y, 1, 1)];
        [_objects addObject:rectangle];
        _currentPanObject = rectangle;
        [self addSubview:rectangle];
    }
    else if ([sender state] == UIGestureRecognizerStateChanged)
    {
        CGPoint endPanPoint = [sender locationInView:self];

        float height = fabsf(endPanPoint.y - _startPanPoint.y);
        float width = fabsf(endPanPoint.x - _startPanPoint.x);
        float x = MIN(_startPanPoint.x, endPanPoint.x);
        float y = MIN(_startPanPoint.y, endPanPoint.y);

        MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
        [rectangle setFrame:CGRectMake(x, y, width, height)];
        [rectangle setNeedsDisplay];
    }
    else if ([sender state] == UIGestureRecognizerStateEnded)
    {
        CGPoint endPanPoint = [sender locationInView:self];

        float height = fabsf(endPanPoint.y - _startPanPoint.y);
        float width = fabsf(endPanPoint.x - _startPanPoint.x);
        float x = MIN(_startPanPoint.x, endPanPoint.x);
        float y = MIN(_startPanPoint.y, endPanPoint.y);

        MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
        [rectangle setFrame:CGRectMake(x, y, width, height)];
        [rectangle setNeedsDisplay];
    }
}
0

You're gonna want to call [super drawRect:rect]

Max
  • 577
  • 5
  • 13
  • I am assuming you are referring to adding the super call within the drawRect method of MNTRectangle. That doesn't fix it (I've tried it) and I don't think I need to call the super method in drawRect anyways since all of what I am drawing, I am implementing myself. –  Dec 07 '12 at 22:41
0

To clarify for anyone reading, calling [rectangle setNeedsDisplay]; is needed because you're overriding drawRect: and thus this subclass of UIView, MNTRectangle, needs to be told to manually redraw itself after it's internal variables have been changed.

This function is called automatically on standard objects that inherit from UIView, but since you've created a custom object, you'll need to call it manually if you have some special behavior in MNTRectangle that requires a redraw.

Further documentation here.

JaredH
  • 2,338
  • 1
  • 30
  • 40