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);
}
}
I guess what you are searching for is:
myScrollViewInstance.opaque = NO
After that the background of your scroll view should not be black anymore.
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.
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
}
This should help
CGContextSetFillColorWithColor(context, colorBack);
CGContextFillRect(context, self.bounds);
// Choose bounds and colorBack accordingly
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);
}
}
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.
Related
I have taken a UiView subclass but it's background color is not changing
This class I am using these method
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:CGRectMake(50, 80, 200, 200)];
if (self)
{
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor darkGrayColor]setFill];
CGContextClearRect(context, rect);
CGContextSetLineWidth(context, 2.0);
CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor);
CGContextMoveToPoint(context, 1, 1);
CGContextAddLineToPoint(context, 100, 100);
CGContextStrokePath(context); // and draw
}
I am calling this class to main view controller
Newclass *obj =[[Newsclass alloc]init];
obj.backgroundColor =[UIColor redColor];
[self.view addsubview :obj];
but background color by default show black color. It is not changing
if i implemented background-color code init with frame method or
draw rect method than not changing.
if i code it
- (id)initWithCoder:(NSCoder*)aDecoder
than it's method not called
please suggest me UIview subclass black background color how to change
First of all, you should do the color part in UIView file.
if (self = [super initWithFrame:CGRectMake(220,60, 100, 300)])
{
self.backgroundColor =[UIColor greenColor];
}
how ever you don't need to do in this case... ;)
it will definitely change your background color. But what happens next is that Draw rect method is called ,your color is no more visible as the draw rect method draws a view above it (whatever you draw in that method).
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGRect rectangle = CGRectMake(0,0,200,300);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rectangle);
}
You just cannot directly set the background color of cgcontext, you have to fill that context with color.
please make sure that you replace rectangle by rectangular frame of your view. It works like a charm for me.
How to draw a line on the UIImageView? I am using the UIImageView Sub Class, Its not support drawRect: method. I want to draw a line on the image view. How can I do this? Please help me. I am using below code for draw line.
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 5.0f, 5.0f);
CGContextAddLineToPoint(c, 50.0f, 50.0f);
CGContextStrokePath(c);
}
The following code works by creating a new image the same size as the original, drawing a copy of the original image onto the new image, then drawing a 1 pixel line along to the top of the new image.
// UIImage *originalImage = <the image you want to add a line to>
// UIColor *lineColor = <the color of the line>
UIGraphicsBeginImageContext(originalImage.size);
// Pass 1: Draw the original image as the background
[originalImage drawAtPoint:CGPointMake(0,0)];
// Pass 2: Draw the line on top of original image
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, originalImage.size.width, 0);
CGContextSetStrokeColorWithColor(context, [lineColor CGColor]);
CGContextStrokePath(context);
// Create new image
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// Tidy up
UIGraphicsEndImageContext();
First, I wouldn't use a UIImageView. In fact the docs say...
If your subclass needs custom drawing code, it is recommended you use
UIView as the base class.
Use a UIView.
In the UIView add a UIImageView subView and put the image in there. Now you can do you custom drawing in the drawRect method of the UIView and it will appear on top of the image.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 5.0);
CGContextStrokeRect(context, self.bounds);
}
If this isn't working then the drawRect method probably isn't being called. Set a breakpoint to test it.
The best way to go about it in my opinion would be to take a invisible UIView of the same size as UIImageView for drawing and implement the touch methods on the UIView for drawing.
you can use something like:
CGPoint temp=[touch locationInView:self];
Use this in methods like:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
Store all the points in an array and then draw those points on the UIImageView as it is.
Refer to Muhammad Saad Ansari
With Swift
func drawLines ( originalImage:UIImage, lineColor:CGColor ) -> UIImage{
UIGraphicsBeginImageContextWithOptions(originalImage.size,false,0.0)
originalImage.drawInRect(CGRectMake( 0, 0, originalImage.size.width, originalImage.size.height ))
let context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 5.0);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, originalImage.size.width, 0);
CGContextSetStrokeColorWithColor(context,lineColor);
CGContextStrokePath(context);
// Create new image
var newImage = UIGraphicsGetImageFromCurrentImageContext();
// Tidy up
UIGraphicsEndImageContext();
return newImage
}
I have a progress view that I'm trying to animate the change in progress. How do you go about doing that? Below is the code to draw the progress.
Thanks in advance
- (void)drawRect:(CGRect)rect {
// Drawing code.
CGContextRef context = UIGraphicsGetCurrentContext();
// draw the background
CGContextSaveGState(context);
UIBezierPath *outerPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:0];
[outerPath addClip];
CGPoint backgroundStartPoint = CGPointMake(0.0f, 0.0f);
CGPoint backgroundEndPoint = CGPointMake(0.0f, CGRectGetHeight(rect));
CGContextDrawLinearGradient(context, _backgroundGradient, backgroundStartPoint, backgroundEndPoint, 0);
CGContextRestoreGState(context);
// draw the progress
CGContextSaveGState(context);
UIBezierPath *innerPath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, 1.0f, 1.0f) cornerRadius: 0];
[innerPath addClip];
[_glossTintColor setFill];
CGRect progressRect = CGRectMake(0, 0, CGRectGetWidth(rect)*_progress, CGRectGetHeight(rect));
CGContextFillRect(context, progressRect);
CGContextRestoreGState(context);
}
Whenever the progress changes, trigger a redraw using [view setNeedsDisplay];
See the UIView reference on setNeedsDisplay.
For example, lets say that custom view you have created has a property called progress, the instance variable is called _progress. Lets also suppose that property gets updated by some event/timer/additional thread.
- (void)setProgress:(float)progress
{
_progress = progress;
[self setNeedsDisplay];
}
That overrides the original setter and adds the functionality mentioned above.
what I'm trying to do is create a custom progress bar, with quartz drawing, what I do is the following,
- (void)drawRect:(CGRect)rect {
// Drawing code.
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect currentBounds = self.bounds;
CGContextSetLineWidth(context, 20.0f);
CGContextSetStrokeColorWithColor(context, [ProgressBarView redColor]);
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(currentBounds), CGRectGetMidY(currentBounds));
CGContextAddLineToPoint(context, CGRectGetMaxX(currentBounds) * time, CGRectGetMidY(currentBounds));
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathStroke);
}
In the CGContextAddLineToPoint I multiply the maxX times time, which is calculated with this function every second.
- (void)pushTime {
if (time >= 1.0) {
time = 0.0;
} else {
time = time += 0.1;
}
[self setNeedsDisplay];
}
It works great the first time, but then the progress bar never gets back to the starting point, I already tried to change the start value to other than 0, so that quartz can create the path without a problem, but that didnt do it.
Anyway, thank you for your help.
It seems your view does not clear previously drawn content for some reason - you can fix that by explicitly forcing view to clear before redrawing itself:
- (void)drawRect:(CGRect)rect {
// Drawing code.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
...
Although I expected that clearing should be done automatically (and tweaking the value of clearsContextBeforeDrawing property does not make any effect...)
I want to place some "handles" on a UIView I have on-screen.
So, I'm creating more UIViews, one at each corner of the UIView's frame rectangle, and I simply want to draw a circle that "fills" the rectangle that is the frame of the "handle" UIView.
Here's what I mean:
How I create the "HandleView":
CGPoint upperLeft = CGPointMake([[self viewToMove] frame].origin.x - 5, [[self viewToMove] frame].origin.y - 5);
HandleView *uL = [[HandleView alloc] initWithFrame:CGRectMake(upperLeft.x, upperLeft.y, 10, 10)];
[self setUpperLeftHandle:uL];
[uL release];
[[self view] addSubview:[self upperLeftHandle]];
drawRect: for HandleView:
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor orangeColor] set];
CGContextFillEllipseInRect(context, [self frame]);
}
What I'm getting is just a set of black rectangles where I place the HandleViews. I'm not sure why they're black, but I can change the color by changing the [HandleView backgroundColor] property. I cannot, though, get anything to DRAW on this view. Calling setNeedsDisplay doesn't seem to make any difference.
Also, the drawRect: method IS being called, so there's a problem with the code there, probably not anywhere else.
It's been a while since I've messed with custom drawing, but I don't remember it being this hard.
What am I missing?
Thanks!!!
Update:
Modified code to this:
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
[self setBackgroundColor:[UIColor clearColor]];
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 100, 100, 100, 1.0);
CGContextFillEllipseInRect(context, rect);
}
Now, I see the circle, but only where it overlays another view. Where it's just sitting on top of the "background" UIView, I get nothing I can see. Also, even though the color is (100, 100, 100), it shows up white/light-light-gray.
What's the deal?
This might work:
- (void)drawRect:(CGRect)rect {
// Drawing code
[[UIColor orangeColor] setFill];
[[UIBezierPath bezierPathWithOvalInRect:rect] fill];
}
The problem is with the following line:
CGContextFillEllipseInRect(context, [self frame]);
You need to change this to the following:
CGContextFillEllipseInRect(context, [self bounds]);
Origin of frame is the left-top corner location of view in the parent. So, it can be any value within the limits of the size of parent view.
So, while drawing with frame, you will be offsetting Fillellipse rect location which happens to be beyond the visible rect of the handle view.
Hence, you were not able to see anything.
As for the color showing up as white, it's because CGContextSetRGBFillColor() expects values from 0.0 to 1.0 for the color component values (not 0-255). So, by passing 100, you were effectively passing 1.0, which was creating a white color.