I try to use the anchorPoint in a layer hierarchy to move a layer with its sublayers. Unfortunately the sublayer did not move together with the root layer. In a custom NSView is set up my layer hierarchy like in the following snippet.
CALayer * rootLayer;
rootLayer = [[CALayer layer] retain];
rootLayer.position = CGPointMake(...);
[self.layer addSublayer:rootLayer];
subLayer = [[MapLayer layer] retain];
subLayer.position = CGPointMake(...);
[rootLayer addSublayer:baseLayer];
While handling a mouse event I want to set the anchorPoint of the rootLayer to move the whole layer hierarchy:
rootLayer.anchorPoint = CGPoint(0.7, 0.7);
I expect from this call, that the rootLayer moves together with its subLayer so that the anchor point is at the center of the view. What happens is, that the sublayer did not move. Only when I call:
rootLayer.anchorPoint = CGPoint(0.7, 0.7);
subLayer.anchorPoint = CGPoint(0.7, 0.7);
the layers behave as expected.
I thought setting the anchor point of the root layer would be enough. This is used in my Map application for OS X. There I set up the view and use the anchorPoint to move the whole map. With only one sublayer in the custom NSView the application behaves as expected.
Thanks in advance.
UPDATE: After a discussion with a colleague I set the bounds of the rootLayer and now it works.
CALayer * rootLayer;
rootLayer = [[CALayer layer] retain];
rootLayer.bounds = CGRectMake(0, 0, 256, 256);
rootLayer.position = CGPointMake(...);
[self.layer addSublayer:rootLayer];
// ...
Related
I'm trying to arrange views one above another, so that front view would appear with some animation. It looks like the only way to achieve that is to use CALayer. Here is code that I use
-(void)viewDidLoad {
CALayer *frontLayer = [CALayer layer];
CALayer *rearLayer=[CALayer layer];
frontLayer.backgroundColor=CGColorCreateGenericRGB( 0.25, 0.45, 0.35, 1.0 );
frontLayer.frame = CGRectMake(0,311, 266,21);
[_outputText setLayer:rearLayer];
[_outputText setWantsLayer:YES];
[_outPutTextHideBox setLayer:frontLayer];
[rearLayer addSublayer:frontLayer];
}
Here _outputText is NSTextView and _outPutTextHideBox is NSBox.
The problem is that my frontLayer is not displayed on the top of rearLayer. Actually, frontLayer is displayed under rearLayer.
I am trying to add a NSButton on a layer inside a IKImageBrowserCell object. I found this post helpful but it doesn't get into the crux.
I've already tried this:
- (CALayer *) layerForType:(NSString*) type
{
CGColorRef color;
//retrieve some usefull rects
NSRect frame = [self frame];
NSRect imageFrame = [self imageFrame];
NSRect relativeImageFrame = NSMakeRect(imageFrame.origin.x - frame.origin.x, imageFrame.origin.y - frame.origin.y, imageFrame.size.width, imageFrame.size.height);
/* foreground layer */
if(type == IKImageBrowserCellForegroundLayer){
//no foreground layer on place holders
if([self cellState] != IKImageStateReady)
return nil;
//create a foreground layer that will contain several childs layer
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
//add a checkbox to tell whether to upload this one or not
NSRect checkFrame = NSMakeRect( ( frame.size.width/2)-5 , frame.size.height - 19, 18,18);
NSButton *uploadCheckBox = [[NSButton alloc] initWithFrame:checkFrame];
[uploadCheckBox setButtonType:NSSwitchButton];
[layer addSublayer :[uploadCheckBox layer]];
return layer;
}
//(...)
return nil;
}
But unfortunately the button doesn't appear on the layer. I think the placement of the button is fine, since it's based on an example code from Apple's app. I've got a feeling that this line is wrong:
layer addSublayer :[uploadCheckBox layer]];, since I should be adding entire NSButton, not just it's bitmap representation (a layer). Any help greatly appreciated!
You cannot add a NSView inside a CALayer. You should create a new layer for your button purpose and add to your holding layer.
Is it possible to modify the anchorPoint property on the root CALayer of a layer-backed NSView?
I have a view called myView and it seems every time I set the anchorPoint, it gets overridden in the next run loop. I am doing this:
NSView *myView = [[myView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
//set the root layer
myView.layer = [CALayer layer];
myView.wantsLayer = YES;
//gets overridden on the next run loop
myView.layer.anchorPoint = CGPointMake(1,1);
On 10.8, AppKit will control the following properties on a CALayer
(both when "layer-hosted" or "layer-backed"): geometryFlipped, bounds,
frame (implied), position, anchorPoint, transform, shadow*, hidden,
filters, and compositingFilter. … Use the appropriate NSView cover
methods to change these properties.
Basically it will set the anchor to [0,0] from [0.5,0.5], to account for this i uses something like :
+(void) accountForLowerLeftAnchor:(CALayer*)layer
{
CGRect frame = layer.frame;
CGPoint center = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
layer.position = center;
layer.anchorPoint = CGPointMake(0.5, 0.5);
}
This is causing me some pain...
I wish to use layer-hosting views in my app and I'm having this weird problem.
Here is a simple example. Simply implemented by creating a new project in Xcode and entering the following in the AddDelegate: (after adding QuartzCore to the project):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSView *thisView = [[NSView alloc] initWithFrame:CGRectInset([self.window.contentView bounds], 50, 50)];
[thisView setLayer:[CALayer layer]];
[thisView setWantsLayer:YES];
thisView.layer.delegate = self;
thisView.layer.backgroundColor = CGColorCreateGenericRGB(1,1,0,1);
thisView.layer.anchorPoint = NSMakePoint(0.5, 0.5);
[self.window.contentView addSubview:thisView];
//Create custom content
[thisView.layer display];
}
I also implement the following CALayer Delegate method:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
[[NSColor blueColor] setFill];
NSBezierPath *theBez = [NSBezierPath bezierPathWithOvalInRect:layer.bounds];
[theBez fill];
}
If I run this code, I can see the subview being added to the windows contentView (big yellow rectangle), and I'm supposing it is a layer-hosting view... and I can see the oval being drawn in blue, but it is underneath the yellow rectangle, and it's origin is at (0,0) in the main Window... it is like it is not actually being drawn inside the yellow layer.
I'm guessing either my view is not really layer-hosting, or that the context being passed to the layer is wrong... but why would it be underneath?
I must be doing something wrong...
To continue with the weirdness, if I add a CABasicAnimation to the layer, like so:
CABasicAnimation *myAnimation = [CABasicAnimation animation];
myAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
myAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
myAnimation.fromValue = [NSNumber numberWithFloat:0.0];
myAnimation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
myAnimation.duration = 1.0;
myAnimation.repeatCount = HUGE_VALF;
[thisView.layer addAnimation:myAnimation forKey:#"testAnimation"];
thisView.layer.anchorPoint = NSMakePoint(0.5, 0.5);
The yellow background gets animated, rotating about its center, but the blue ellipse gets drawn correctly inside the layer's frame (but also outside, at the origin of the Window, so it is there twice) but does not animate. I would expect the ellipse to rotate with the rest of the layer of course.
I have made this project available here for those willing to give a hand.
Renaud
Got it. I was confused by the fact that the context being called in this situation is a CGContextRef, not an NSGraphicsContext!
I seem to be able to get the result I need by setting the NSGraphicsContext from the CGContextRef:
NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:NO];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:gc];
//Insert drawing code here
[NSGraphicsContext restoreGraphicsState];
How to shadow documentView in NSScrollView?
The effect look likes iBook Author:
You need to inset the content in your document view to allow space for the shadow to be displayed, then layer back the view and set a shadow on it. Example:
view.wantsLayer = YES;
NSShadow *shadow = [NSShadow new];
shadow.shadowColor = [NSColor blackColor]
shadow.shadowBlurRadius = 4.f;
shadow.shadowOffset = NSMakeSize(0.f, -5.f);
view.shadow = shadow;
The NSScrollView contentView is an NSView subclass, which has a shadow field, if you create a shadow object and assign it to this field, the view will automatically show a drop shadow when drawn
NSShadow* shadow = [[NSShadow alloc] init];
shadow.shadowBlurRadius = 2; //set how many pixels the shadow has
shadow.shadowOffset = NSMakeSize(2, -2); //the distance from the view the shadow is dropped
shadow.shadowColor = [NSColor blackColor];
self.scrollView.contentView.shadow = shadow;
This works because all views when are drawn on drawRect use this shadow property by using [shadow set].
doing [shadow set] during a draw operation makes whatever is drawn after that to be replicated underneath
I'm new to entering posts on stack overflow but I had the same issue and have solved it so I thought after searching the net for hours to find a solution it would be nice to answer it.
My solution is to create a subclass for NSClipView with the following code for drawRect...
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
NSRect childRect = [[self documentView] frame];
[NSGraphicsContext saveGraphicsState];
// Create the shadow below and to the right of the shape.
NSShadow* theShadow = [[NSShadow alloc] init];
[theShadow setShadowOffset:NSMakeSize(4.0, -4.0)];
[theShadow setShadowBlurRadius:3.0];
// Use a partially transparent color for shapes that overlap.
[theShadow setShadowColor:[[NSColor grayColor]
colorWithAlphaComponent:0.95f]];
[theShadow set];
[[self backgroundColor] setFill];
NSRectFill(childRect);
// Draw your custom content here. Anything you draw
// automatically has the shadow effect applied to it.
[NSGraphicsContext restoreGraphicsState];
}
You then need to create an instance of the subclass and set it with the setContentView selector.
You also need to repaint the clip view every time the content view size changes. If you have your content view set up to change in terms of canvas size when the user wants then unless you repaint the clip view some nasty shadow marks will left behind.
You don't need to mess about with clips as others have suggested.
Hope it helps!