When applying an NSAffineTransform rotate and translate to an NSRect, none of the sub views are affected by the transform. I've included the code below.
- (void)drawRect:(NSRect)dirtyRect
{
NSAffineTransform * transform = [NSAffineTransform transform];
[transform translateXBy:13.0 yBy:3.0];
[transform rotateByDegrees:-45];
[transform translateXBy:-13.0 yBy:3.0];
[transform concat];
NSImageView *imageViewWatchPointer;
NSRect watchPointer = NSMakeRect(0, 0, 134, 49);
imageViewWatchPointer = [[NSImageView alloc] initWithFrame:watchPointer];
[imageViewWatchPointer setImageScaling:NSScaleNone];
[imageViewWatchPointer setImage:[NSImage imageNamed:#"mastery_watch_pointer"]];
[self addSubview:imageViewWatchPointer];
[[NSColor blueColor] set];
NSRectFill(watchPointer);
}
You're not applying transform to anything. You're creating it and then throwing it away. In any case, you never add subviews in drawRect:. drawRect: is for custom-drawing the current view, not for managing the view hierarchy. The way you've done this, you're adding a new NSImageView every time the main view is redrawn.
I recommend going back to the Cocoa Drawing Guide to understand how Cocoa drawing works.
Looking at your code, I don't believe you need drawRect: at all. You just want to add a subview, probably in awakeFromNib or initWithFrame:. I assume you want the view rotated. The easiest way to do that is to give it a backing layer (setWantsLayer:), and then set the affineTransform on the layer.
Alternately, in drawRect:, you could rotate the context and just draw the image itself (without using an NSImageView). See the Cocoa Drawing Guide for details on how to do custom drawing.
Related
As mentioned in the title of this post I want to set the border (color and width) of a subclassed NSTableCellView which is used inside a view-based NSTableView. I tried the following
self.layer.borderColor = [[NSColor greenColor] CGColor];
self.layer.borderWidth = 3.0;
I placed the code in initWithCoder and awakeFromNib without the desired result. Changing the backgroundColor is possible within the drawRect-Method. Can someone point me to the right direction?
Thanks
EDIT
Here is my solution using NSFrameRect
- (void)drawRect:(NSRect)dirtyRect
{
[NSGraphicsContext saveGraphicsState];
[[NSColor lightGrayColor]set];
NSFrameRect([self bounds]);
[NSGraphicsContext restoreGraphicsState];
}
Views on OS X are not layer backed by default. You first need to setWantsLayer: YES
But if you are using drawRect: you can just use NSFrameRect() or one of the similar functions or draw with an NSBezierPath in you cell view subclass. However, keep in mind that usually the row view does background drawing in view based tables.
Sounds like you have a bit of learning yet to do about drawing in Cocoa.
My UIView structure:
I have a "master" UIView (actually UIScrollView, but it's only purpose scrolling per pagination).
As a subView on this "master" I have my "pageView" (subclass of UIScrollView). This UIScrollView can have any content (e.g. a UIImageView).
The "master" has another subView: PaintView (subclass of UIView). With this view, I track the finger movements and draw it.
The structure looks like this:
[master.view addSubview: pageView];
[master.view addSubview: paintView];
I know when the user zooms in (pageView is responsible for this) and over delegate/method calls I change the frame of paintView according to the zoom change, during the zoom action.
After zooming (scrollViewDidEndZooming:withView:atScale) I call a custom redraw method of paintView.
Redraw method and drawRect:
-(void)redrawForScale:(float)scale {
for (UIBezierPath *path in _pathArray) {
//TransformMakeScale...
}
[self setNeedsDisplay];
}
-(void)drawRect:(CGRect)rect {
for (UIBezierPath *path in _pathArray) {
[path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
}
The problem:
When I zoom-in, I receive a memory warning and the app crashes.
In the Allocations profiler I can see that the app own a lot of memory, but I can't see why.
When I don't call 'setNeedDisplay' after my 'redrawForScale' method the app isn't crashing.
When I log the rect in drawRect I see values like this: {{0, 0.299316}, {4688, 6630}}.
The problem is (re)drawing such a huge CGRect (correct me if this is wrong).
The solution for me is to avoid calling a custom drawRect method. In some stackoverflow answers (and I think somewhere in the Appls docs) it was mentioned that a custom drawRect: will reduce the performance (because iOS can't do some magic on a custom drawRect method).
My solution:
Using a CAShapeLayer. For every UIBezierPath I create a CAShapeLayer instance, add the UIBezierPath to the path attribute of the layer and add the layer as a sublayer to my view.
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] initWithLayer:self.layer];
shapeLayer.lineWidth = 10.0;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.path = myBezierPath.CGPath;
[self.layer addSublayer:shapeLayer];
With this solution I don't need to implement drawRect. So the App won't crash, even with a big maximumZoomScale.
After changing the UIBezierPath (translate, scale, change color) I need to set the changed bezierPath to the shapeLayer again (set the path attribute of shapeLayer again).
I'm having difficulties understanding why the below code doesn't work, what I want to achieve is an image being displayed in the top left corner of a NSView, but nothing is showing...
NSImage *map0 = [NSImage imageNamed:#"map0.png"];
NSRect rect = NSMakeRect(0, 0, 400, 400);
[map0 drawInRect:rect fromRect:NSZeroRect operation:NSCompositeSourceAtop fraction:1.0f];
[map drawRect:rect];
EDIT:
map is the NSView into which I would like to draw the image into
You never call drawRect: directly. This routine has various pre-conditions that are provided by Cocoa, such as the creation of a CGContextRef. You implement drawRect:. Cocoa calls it.
Your drawInRect:fromRect:operation:fraction: call should be put into the drawRect: of map, which should be a subclass of NSView. This specific problem is usually better solved with an NSImageView rather than a custom NSView, but if the drawing is more complex, then a custom NSView is appropriate.
I'm looking for a way to draw a fade effect on a table view (and outline view, but I think it will be the same) when the content is scrolled. Here is an example from the Fantastical app:
Also a video of a similar fade on QuickLook windows here.
To make this I tried subclassing the scrollview of a tableview with this code:
#define kFadeEffectHeight 15
#implementation FadingScrollView
- (void)drawRect: (NSRect)dirtyRect
{
[super drawRect: dirtyRect];
NSGradient* g = [[NSGradient alloc] initWithStartingColor: [NSColor blackColor] endingColor: [NSColor clearColor]];
NSRect topRect = self.bounds;
topRect.origin.y = self.bounds.size.height - kFadeEffectHeight;
topRect.size.height = kFadeEffectHeight;
NSRect botRect = self.bounds;
botRect.size.height = kFadeEffectHeight;
[NSGraphicsContext saveGraphicsState];
[[NSGraphicsContext currentContext] setCompositingOperation: NSCompositeDestinationAtop];
// Tried every compositing operation and none worked. Please specify wich one I should use if you do it this way
[g drawInRect: topRect angle: 90];
[g drawInRect: botRect angle: 270];
[NSGraphicsContext restoreGraphicsState];
}
...but this didn't fade anything, probably because this is called before the actual table view is drawn. I have no idea on how to do this :(
By the way, both the tableview and the outlineview I want to have this effect are view-based, and the app is 10.7 only.
In Mac OS X (as your question is tagged), there are several gotchas that make this difficult. This especially true on Lion with elastic scrolling.
I've (just today) put together what I think is a better approach than working on the table or outline views directly: a custom NSScrollView subclass, which keeps two "fade views" tiled in the correct place atop its clip view. JLNFadingScrollView can be configured with the desired fade height and color and is free/open source on Github. Please respect the license and enjoy. :-)
I have a class called mapWindow which is hooked up to a window in IB.
No matter what, the red circle which I want the program to render won't show up unless the code is under drawRect or I move the window borders. Not even unlocking and locking the focus updates the window.
theOtherWindowView is actually a NSView hooked up to a custom view in IB.
- (void)test
{
[theOtherWindowView lockFocus];
NSBezierPath *path = [NSBezierPath bezierPath];
NSPoint center = [self drawPoint];
[path moveToPoint: center];
[path appendBezierPathWithArcWithCenter:center
radius:explosionRadius
startAngle:0
endAngle:360];
[[NSColor redColor] set];
[path fill];
[theOtherWindowView unlockFocus];
}
I don't want to use drawRect because I want multiple instances not one shape that has it's coordinates changed every update.
I've also tried [self lockFocus] and [mapWindow lockFous]
Keep doing your drawing in -drawRect:. When -drawRect: is sent, your view's coordinate system and clipping boundaries will have been set up for you, and your window's drawing context will be the current one.
In that method, draw as many of these circles as you want.