Multiple CALayer instances created by a UIScrollView? - ios

I'm working on a project where I'm using a CAShapeLayer to render a grid behind some content. The problem is that the OS creates multiple instances of this CAShapeLayer subclass, when I'm expecting one.
First, I subclassed CAShapeLayer, overriding init and layoutSublayers (the layer has a few sublayers that render additional content).
Then, I overrode +[UIView layerClass] in the view that uses my CAShapeLayer subclass.
Lastly, the view using my layer was added as a subview to a UIScrollView.
To reference self.layer (in the view) with the correct class (GridLayer vs CALayer), I created the following property:
#property (nonatomic, weak, readonly, getter=graphLayer) GraphLayer *graphLayer;
and getter
- (GraphLayer *)graphLayer {
return (GraphLayer *)self.layer;
}

Related

make custom CALayer subclass automatically redraw itself after being added to a super layer

I am subclassing CALayer and doing custom drawing in drawInContext: method.
The problem is I need to manually call setNeedsDisplay: after I add an instance of my subclass to a superlayer, something like:
MyCustomLayer *instance = [MyCustomLayer layer];
//do some configurations
[self.view.layer addSublayer:instance];
[layer setNeedsDisplay]; //required
or the drawing will not happen.
But when I use buit-in CALayer subclasses (CATextLayer, CAShapeLayer etc.), the call to setNeedsDisplay: is not needed after adding them to a super layer. Can I make my subclass behaves like those classes?
You can call displayIfNeeded on yourself within the init method, however better is to subclass needsDisplayForKey: and return YES for the #"transform" key, which would redraw when the bounds changes (i.e. when first added to the view).

How to implement drawLayer:InContext in the implementation file of a UIView?

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.

Draw to UIView subview using an instance of that subview

I am trying to draw to a UIView subview from another class.
MainViewController with a view that I have dragged a UIView into in IB.
I have created a DrawView class and changed the UIView's(the one I dragged onto the MainController's view in IB earlier) class to DrawView.
I can draw to that DrawView UIView subview by coding in DrawView so that works as expected.
I have a custom UITableViewController class and would like to draw in the "DrawView UIView" from the custom UITableViewController. How to do so?
I have tried creating a new instance of DrawView from the custom UITableViewController and creating functions in DrawView that I can call to draw. Doesn't work because my created instance of DrawView is a different instance than the "DrawView UIView" in IB that is created on launch. The newly created DrawView's bounds are zero, too, passing in bounds for initWithFrame doesn't work.
I don't understand how I can get the same instance of DrawView created in IB to use in the custom UITableViewController.
In case the above is too wordy, I want to call drawing methods to draw in a UIView subview in its class from another class.
How can I implement this? Thanks.

Using CATransformLayer warning: changing property opaque in transform-only layer, will have no effect

I created a "TransformView" subclassing UIView in order to support a double-sided view which I am using in a flip animation. This has been suggested in other posts, eg:
How to rotate a flat object around its center in perspective view?
#interface TransformView : UIView
#end
#implementation TransformView
+ (Class)layerClass {
return [CATransformLayer class];
}
#end
It all works fine, but I get a warning every time I create a TransformView object using:
TransformView *newTransformView=[[TransformView alloc] initWithFrame:frame];
The warning says:
- changing property opaque in
transform-only layer, will have no effect
I guess the UIView class is initialising the opaque property which is usually fine for a CALayer but not a CATransformLayer.
Is the subclassing code quite dodgy? If so, how else can you create a 2-sided view for flip animations?
Any ideas for how to stop the warning?
I have a suspicion that creating a TransformView from a nib file rather than initWithFrame avoids the warning, but is seem cludgy to have to do this just to avoid the warning.
Your can avoid the warning by adding this extension somewhere in your code:
#implementation CATransformLayer (MyExtension)
-(void)setOpaque:(BOOL)opaque
{
return;
}
#end
Of course this will also stop the warning for your own mistaken attempts to set the opaque property of a CATransformLayer.

CALayer not implementing custom drawLayer:inContext: method

I saw this solution while researching for CALayers. I was looking for a way to implement custom drawings inside a UIView with multiple sublayers. I named my sublayers like this:
layer1.name = #"Back";
layer2.name = #"Middle";
layer3.name = #"Front";
And I created custom methods to be implemented by this layers
-(void)drawBackLayer:(CALayer *)layer inContext:(CGContextRef)ctx
-(void)drawMiddleLayer:(CALayer *)layer inContext:(CGContextRef)ctx
-(void)drawFrontLayer:(CALayer *)layer inContext:(CGContextRef)ctx
The problem is these methods are not implemented, instead the drawLayer:inContext:, which is being used by the view's root layer, is implemented four times. This means that the custom layers implement this method instead of the custom methods. Can anyone explain to me why?
NOTE: the solution I am referring in the link is the code provided by Dave Lee.
The layer used by the UIView must be a custom CALayer that implements those messages. The way you get a UIView subclass to use a particular layer is via the class method "+(Class)layerClass".
So:
- subclass a UIView (or subclass of it like UIImageView, etc)
- implement the layerClass method, and return [MyCALayer class];
Now that view will use YOUR CALayer subclass.

Resources