xonegirlz
xonegirlz

Reputation: 8977

black background when overriding drawRect in UIScrollView

So I am trying to override drawRect in my UIScrolLView, however it gives me this black background instead of the background color that I've specified for my UIScrollView. Why is this? If I remove the drawRect code then everything is fine:

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    if (shouldDrawVerticalLineForProfile){

        CGContextRef context = UIGraphicsGetCurrentContext();
        CGColorRef separatorColor = [UIColor colorWithRed:47.0/255.0 green:47.0/255.0 
                                                     blue:47.0/255.0 alpha:1.0].CGColor;

        // Add at bottom
        CGPoint startPoint = CGPointMake(60, 0);
        CGPoint endPoint = CGPointMake(60, 10000);

        CGContextSaveGState(context);
        CGContextSetLineCap(context, kCGLineCapSquare);
        CGContextSetStrokeColorWithColor(context, separatorColor);
        CGContextSetLineWidth(context, 5.0);
        CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
        CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
        CGContextStrokePath(context);
        CGContextRestoreGState(context);  

    }

}

Upvotes: 22

Views: 13824

Answers (6)

Adam Kaplan
Adam Kaplan

Reputation: 1972

The answer here is very clearly documented in the AppleDocs for UIView's drawRect: method. The first sentence is:

The default implementation of this method does nothing.

So, the suggestions here to call super are not going to help. The rest of the answer is contained further down in the discussion:

... if the opaque property of your view is set to YES, your drawRect: method must totally fill the specified rectangle with opaque content.

Meaning that if you don't intend to have a transparent background, you need to actually draw the background. The most correct way to do this is to use the following drawRect: template:

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect]; // optional if a direct UIView-subclass, should be called otherwise.

    CGContextRef context = UIGraphicsGetCurrentContext();
    // Fill the background color, if needed
    if (self.opaque) {
        UIGraphicsPushContext(context);
        CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor);
        CGContextFillRect(context, rect);
        UIGraphicsPopContext();
    }

    // Your code here...
}

Swift Update

Since you are responsible to add background color on your custom view, make sure to fill rect with color:

override func draw(_ rect: CGRect) {
   super.draw(rect)
   guard let context = UIGraphicsGetCurrentContext() else {
        return
    }
    context.saveGState()
    // Add you custom color
    context.setFillColor(UIColor.white.cgColor)
    context.fill(rect)
    defer { context.restoreGState() }
    
    // Your custom drawing ....

}

By the way, you could also set your custom view background color on init. Then you do not need to fill rect with color as stated above.

override init(frame: CGRect) {
    super.init(frame: frame)
    self.backgroundColor = UIColor.white
}

Upvotes: 11

btmanikandan
btmanikandan

Reputation: 1931

Please try this its Work for me

 - (id)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            [self setBackgroundColor:[UIColor clearColor]];
        }
        return self;
    }
    - (void)drawRect:(CGRect)rect
    {
        [super drawRect:rect];
        if (shouldDrawVerticalLineForProfile){

            CGContextRef context = UIGraphicsGetCurrentContext();
            CGColorRef separatorColor = [UIColor colorWithRed:47.0/255.0 green:47.0/255.0 
                                                         blue:47.0/255.0 alpha:1.0].CGColor;

            // Add at bottom
            CGPoint startPoint = CGPointMake(60, 0);
            CGPoint endPoint = CGPointMake(60, 10000);

            CGContextSaveGState(context);
            CGContextSetLineCap(context, kCGLineCapSquare);
            CGContextSetStrokeColorWithColor(context, separatorColor);
            CGContextSetLineWidth(context, 5.0);
            CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
            CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
            CGContextStrokePath(context);
            CGContextRestoreGState(context);  

        }

    }

Upvotes: 4

scottwb
scottwb

Reputation: 829

The only thing that worked for me was to explicitly set the background after any call to setNeedsDisplay. In my example, I was using a subclass of an MKAnnotationView over a map, instead of a UIScrollView, so I don't really know how well this applies to the OP scenario. In my case, I was calling setNeedsDisplay from setAnnotation, and I found that doing so resets by backgroundColor. Simply re-setting by backgroundColor after setNeedsDisplay fixes the problem.

FWIW, I too observed that simply overriding drawRect to delegate to the super class caused this black background problem.

Upvotes: -1

Denton
Denton

Reputation: 345

I ran into a similar situation when overriding drawRect in my UIScrollView subclass.

Simply overriding with:

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
}

resulted in a black background instead of the desired clear background I'd get without overriding.

I found it was because the View Background was set to Default in Interface Builder and that setting it to Clear Color resolved the issue for me.

I'm not sure if this is related to your problem but thought I'd share in case it might help.

Upvotes: 14

PakitoV
PakitoV

Reputation: 2498

I guess what you are searching for is:

myScrollViewInstance.opaque = NO

After that the background of your scroll view should not be black anymore.

Upvotes: 23

S.P.
S.P.

Reputation: 3054

This should help

CGContextSetFillColorWithColor(context, colorBack);
CGContextFillRect(context, self.bounds); 

// Choose bounds and colorBack accordingly

Upvotes: 4

Related Questions