Drawing NSView's subviews manually - cocoa

I'm writing a custom NSView that needs to animate its subviews by applying NSAffineTransformation before drawing them.
I am looking for a method that draws the view's subviews so I can override it
in a way like that:
-(void)drawSubview:(NSView*)subview {
[[NSGraphicsContext saveGraphicsState];
[[self affineTrasformForSubview: subview] concat];
[subview drawRect: subview.bounds];
[[NSGraphicsContext restoreGraphicsState];
}
Any suggestions?

for rotating: NSView setFrameCenterRotation:angle
for scaling: NSView scaleUnitSquareToSize:size
for more than rotating and scaling you would need to make your view a layer backed one and mess around with the layer, even though that is against apple recommendation. But I wouldn't worry too much about that and I think it is worth a try.

Related

What is the best way to show the content below the NSWindow?

Here is the UI Mock up:
The blue background is the screen, and the red is a NSWindow, and I would like to build a NSView? (Yellow) that is on top of the NSWindow. The NSView is basically transparent, so that it can shows the live information behind the red NSWindow. What is the best way to implement the NSView? Any suggestions? Thanks.
***One more remarks, the Yellow view is not only need to transparent, but also need to use for analysis. So, if I can get the raw data or transfer it to NSImage will be great.
My 2 cents. Let your view be an NSImageView. You can draw the background to be clear
- (void)drawRect:(NSRect)rect
{
[[NSColor clearColor] set];
NSRectFill(rect);
}
You could then convert whatever you draw in there into an image by using [NSBitmapImageRep initWithFocussedViewRect:]. Would something like that work?

NSView with layer is drawn over all content

I have an NSView, which I change some properties to give make it a rounded rectangle, with a gray color, and add it to my view (behind everything)
float gray = 60.0f/255.0f;
NSView* background = [[NSView alloc] initWithFrame: self.iconContainer.frame];
CALayer *viewLayer = [CALayer layer];
[viewLayer setBackgroundColor:CGColorCreateGenericRGB(gray, gray,gray, 1)]; //RGB plus Alpha Channel
[viewLayer setCornerRadius:5.0f];
[background setWantsLayer:YES]; // view's backing store is using a Core Animation Layer
[background setLayer:viewLayer];
// Place view behind all other views
[self.iconContainer addSubview:background positioned:NSWindowBelow relativeTo:nil];
However no matter what I try, that particular view is drawn above everything else.
Usually, this behaviour is caused by the use of Core Animation layers on a subview, while the superview hasn't any.
Enabling Core Animation layers on the superview should fix the issue, as any subviews will then be drawn using Cora Animation layers, making the drawing process respect the order of subviews.

Rendering NSView to image: cacheDisplayInRect draws transparent areas differently from usual drawRect call

Greetings! I have a problem and Googling brought no results...
I implemented the drawRect method for my NSView (subclass) to draw some shadows and semi-transparent fills. Everything looks great! But now I need to create an NSImage from my NSView (make snapshot) for drag&drop purposes.
It works, but draws in some different manner: darker and not so contrast as should be.
Why? Maybe because of NSGraphicContext different options? Need help and/or advice!
Here is the code for getting NSImage from NSView:
- (NSImage *)makeImageSnapshot {
NSSize imgSize = self.bounds.size;
NSBitmapImageRep * bir = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
[bir setSize:imgSize];
[self cacheDisplayInRect:[self bounds] toBitmapImageRep:bir];
NSImage* image = [[[NSImage alloc] initWithSize:imgSize] autorelease];
[image addRepresentation:bir];
return image;
}
And here are the images to compare visually:
Normal - drawn by drawRect usual call: http://cl.ly/image/213C1Y1V0v2H
Bad - captured into NSImage: http://cl.ly/image/183q442S2J14
Thought the difference might seem very small, believe me - it is obvious while working with application. I don't undertand why that is happening. And hope someone can help...
Thanks in advance!
I believe the transparency problem is caused by not having other things to blend it against when it's cached on its own.
Try -dataWithPDFInsideRect: or -dataWithEPSInsideRect: and get your image reps from there.

CATiledLayer to draw giant UIView with buttons?

I've got this giant UIView with around 600 UIButtons as subviews. The UIView is about 2000 by 2000 pixels and it's a subview on itself for a UIScrollView. I'm trying to implement CATiledLayer as the rendering layer for this giant UIView but I can't seem to figure out how to render the tiles. I found a lot of examples that cover CATiledLayer with tiled images, pdf's, ... but I never found a real example on how to draw a complete UIView with a lot of subviews. You're probably asking why i'd like to stick with UIView? Because I'd like the users to keep on using the buttons to interact with the view.
I'm wondering if someone has an example or some psuedo code on how to implement the - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context method, keeping in mind that there's 1 giant UIView with a lot of UIbuttons as its subviews.
Starting with iOS 4.0, you can use drawRect: instead of drawLayer:inContext:. This Q&A from Apple explains it: http://developer.apple.com/library/ios/#qa/qa1637/_index.html. The important point from it is that drawRect: in 4 is now thread-safe and you can use UIKit graphics functionality.
You still need to override this method to use a CATileLayer:
+(Class)layerClass
{
return [CATiledLayer class];
}
But you can now just use drawRect:
-(void)drawRect:(CGRect)rect
{
//normal drawing routines here.
}
The rect that is delivered to the method will be the size of your tiles and you need to determine what needs to be drawn in the particular rect. The power of CATiledLayer is that it only calls for drawing the tiles that are showing on the screen. It also uses background threads and is very fast.
I have not used CATiledLayer with UIViews, only large images. Are the UIViews subclasses with their own drawRect: implementations? I think you will need to determine which views will be showing in the current rect and call their drawRect: method.
Using drawRect: is only for iOS 4.0 and later, it won't work for older versions.

How can I get NSScrollView to respect a clipping path

I am trying to make a NSScrollView with clipped corners, similar to the Twitter app:
I have a NSScrollView subclass which I added the following code:
- (void)drawRect:(NSRect)dirtyRect {
NSBezierPath *pcath = [NSBezierPath bezierPathWithRoundedRect:[self bounds] xRadius:kDefaultCornerRadius yRadius:kDefaultCornerRadius];
[path setClip];
[super drawRect:dirtyRect];
}
I expected the content of the NSScrollView to have rounded corners, but it is not respecting the clipped path. How can I do this?
UPDATE & CLARIFICATION
I know how to make a custom NSScroller, I know how to make it transparent overlay. All I am asking is how to make the NSSCrollView clip its corners, including everything it contains. The NSScrollView is inside a NSView which has a background that could change, meaning a view overlay to fake the rounded corners is not an option.
After much fiddling, I just discovered that NSScrollView's can be made to have rounded corners by simply giving it a backing layer and setting that layer's corner radius provided you also do the same to it's internal NSClipView. Both are required, which now makes sense, since it's the clip view that actually provides the viewable window into the NSScrollView's document view.
NSScrollView * scrollView = ...;
// Give the NSScrollView a backing layer and set it's corner radius.
[scrollView setWantsLayer:YES];
[scrollView.layer setCornerRadius:10.0f];
// Give the NSScrollView's internal clip view a backing layer and set it's corner radius.
[scrollView.contentView setWantsLayer:YES];
[scrollView.contentView.layer setCornerRadius:10.0f];
You can apply a mask to a view's layer:
[myScrollView setWantsLayer: YES];
[myScrollView layer].mask = ...;
The mask is another CALayer. So in this case you'd create a CALayer, set its background colour to opaque, set its bounds to match the scrollview, and give it a cornerRadius of, say, 8.0.
The result would be that the scroll view and all its contents would appear to be masked to a roundrect with a corner radius of 8px.
Have you tried overriding
- (BOOL)isOpaque {
return NO;
}
And setting the scroll view's -setDrawsBackground: to NO and just leave the view without clipping and just draw the corners with [NSColor clearColor] since this will also clear the underlying color and simulate a round effect.

Resources