I have a custom view inheriting from UIView.
In this view, there is an image which is always the same and I only want to draw on top of that.
However, in order for the image to be drawn, I have to call drawInRect every time the draw cycle runs.
- (void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
[image drawInRect:self.bounds];
// drawing....
}
Is there a way to show the image and not to call this method every time drawRect is invoked?
Thank you.
Put the image in a UIImageView underneath your custom UIView instead of drawing it in drawRect:. Make your custom view's background color clearColor so the image shows through where drawRect: doesn't draw anything.
Don't try to make the image view a subview of your custom view. A subview always draws on top of its superview's content. You could make your custom view a subview of the image view, or you could make them siblings.
In the .h file of the custom class do this:
- (void) setup:
Then in the .m do this:
-(void) setup {
//drawing stuff
}
Then in the view did load of whatever the view controller that adds it as a subview is
[myCustomView setup];
Related
I am new to iOS and trying to understand the use for drawRect() in custom UIViews, so I have simple custom view which I initialize from code.I want to update its colors for instance and I see two approaches as shown below. Which one should I use and why?
//VController
CustomView *cv = [[CustomView alloc] initWithFrame:...]
...
[cv updateColors];
//CustomView
-(id) initWithFrame {}
-(id) initWithCoder {}
-(void) updateColors(UIColor *color){ ----(1)
...Draw here with new color ...
view1.backgroundColor = color;
view2.backgroundColor = color;
}
- (void) drawRect{
... draw here with new color ... ---------(2)
view1.backgroundColor = color;
view2.backgroundColor = color;
}
If all you want to do is change the background color of this view or some of its subviews, you absolutely should not misuse drawRect: for this. drawRect: is for when you want to draw the view (i.e. its content) when the system believes its needs refreshing; it is called at many and unpredictable times, and you don't need that - you just need to change the background color, a feature of the view, on demand. Similarly drawRect: is not the place to perform management of subviews.
But if you are going to draw the view's content (e.g. the view displays a circle and you need to draw that circle to portray the view) then you must use drawRect: for that; it is the only place where the view gets a chance to draw itself.
I have a UIView in which I draw things in the drawRect method. I would like to overlay a CALayer over the UIView CALayer (self.layer) that would draw a subset of the things I draw in the drawRect. Basically, I draw a lot of circles in drawRect, and I would like to highlight some them on the overlay. I get the coordinates of the circles from a model object that is a property of the UIView.
My first attempt was to add a sublayer s to the UIView layer, set its delegate to the UIView and call its setNeedsDisplay method when needed, but drawLayer:InContext is called by both layers (s and self.layer). This doesn't work:
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
if (layer == s ) {
// draw stuffs for s
}
else if (layer==self.layer) {
// do nothing
}
}
results in a black UIView. I would like to draw s from a place where I have access to my data model, and other variables useful for the drawing (width of lines..). I have kinda solved the problem by moving the drawing of s to a separate object, but this forces me to also set a pointer of the model in this object, and also copy other parameters.
So my question is: in a UIView drawing its content using drawRect, how to add a sublayer which delegate is the same UIView ?
You need to subclass UIView and in that subclass override + (Class)layerClass with your own CALayer subclass. Then that UIView will use that subclass when it creates its backing layer. Your CALayer subclass would then have an override for drawLayer.
You can put this CALayer subclass (interface and implementation) in your UIView subclass.
Is it possible to animate a UIView's CoreGraphics content?
Say I have a UIView subclass called MyView that implements the drawRect: method like so:
- (void) drawRect: (CGRect) rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(c, someColor);
CGContextSetLineWidth(c, someWidth);
CGContextMoveToPoint(c, leftOfMyUIView, topOfMyUIView);
CGContextAddLineToPoint(c, leftOfMyUIView, bottomOfMyUIView);
CGContextStrokePath(c);
}
In other words - it draws a vertical line down the left hand side of my subclass of UIView. Is there an easy way to now animate this UIView so that the line moves from the left side to the right side of the view? I would like it to 'drift' from left to right.
To be clear - for reasons I will not go into I cannot move/animate the instance of MyView. The best solution I can think of using is to add a new UIView subclass. Something like LineView extends UIView and then make LineView the exact dimensions of the line I want to draw, fill it using its own drawRect method and then add an instance of LineView to MyView as a subview. This would allow me to animate the position of the LineView object using an animation block, but seems overly complicated to achieve something so simple.
Based upon your comment that there will be hundreds of LineView instances, it may be best to use CALayer subclasses as opposed to UIView subclasses. UIView objects give you event handling, and on iOS each UIView owns a CALayer to handle its rendering. If you don't want/need event tracking for the individual lines, all those UIView instances are probably needless overhead. You can access the parent UIView's layer, and add your line sublayers as needed.
If I understand your problem correctly, there should be no need for CoreGraphics. You can set the line layers' backgroundColor to handle the fill. Or if the lines will not be straight, you can use CAShapeLayer to render the paths of the lines.
Is it possible to overlay UIView with an image without using UIImageView from the Interface Builder?
If not, how would you accomplish the same in code?
From the UIView documentation:
Image-based backgrounds - For views that display relatively static content, consider using a UIImageView object with gesture recognizers instead of subclassing and drawing the image yourself. Alternatively, you can also use a generic UIView object and assign your image as the content of the view’s CALayer object.
Using the second option, to set someView to have a background of yourImage.png:
someView.layer.contents = (id)[[UIImage imageNamed:#"yourImage.png"] CGImage];
if by Overlying a UIView you mean painting its background with an image, it can be done like this:
someView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"yourImage.png"]];
You could also add a new UIImageView that covers your UIView
by code, Init your UIImageView with the same frame Rect as your UIView and add it as a subview.
Is there a way to black out a region (defined by a frame) of a UIImageView without creating an overlaying UIView instance (with this frame) and blacking that out?
Is there similarly a way to reveal part of a UIImageView without using UIView instances to black out the rest of the image?
I am going to cheat a bit here and suggest that you use layer as they are light weight.
CALayer *blackLayer = [CALayer layer];
blackLayer.backgroundColor = [UIColor blackColor];
blackLayer.frame = imageView.bounds;
[imageView.layer addSublayer:blackLayer];
For the second part you can consider using a grid of layers (black). When user touches the image view, you can pick the layers from the area he has touched and remove them from the super layer i.e. the image view's root layer.
you can subclass the UIIMageView and draw as per your needs in the drawRectMethod.
You can create a subclass of UIIMageView and have your definition of drawRectMethod over there.
You can also invoke the drawRectMethod at your will by calling setNeedsLayout method on the view .( setNeedsDisplay - causes drawRect to be called - . Only contentMode = UIViewContentModeRedraw will cause setNeedsDisplay to be called when a view's bounds changes).
UPDATE:
http://developer.apple.com/library/ios/#documentation/CoreGraphics/Reference/CoreGraphics_Framework/_index.html