NSTextField - Subclassing & drawRect cause text not to load - objective-c

Trying to round out the border of an NSTextField (the little black box in the upper-left corner): http://cl.ly/image/2V2L1u3b3u0G
So I subclassed NSTextField:
MYTextField.h
#import <Cocoa/Cocoa.h>
#interface HATrackCounterField : NSTextField
#end
MYTextField.m
#import "HATrackCounterField.h"
#implementation HATrackCounterField
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor blackColor] setFill];
[[NSBezierPath bezierPathWithRoundedRect:dirtyRect xRadius:3.0 yRadius:3.0] fill];
}
#end
Now its not showing the text-field text: http://cl.ly/image/1J2W3K431C04
I'm new at objective-c, it seems like this should be easy, so I'm probably just doing something wrong...
Thanks!
Note: I'm setting the text through a collection view, and I've tried setStringValue: at different points also to no avail.

Your text-field's text isn't showing because you overwrite -drawRect and don't call [super drawRect:dirtyRect] in it.
In your case, I think the easiest way to do what you want is using clip mask: just let NSTextField perform drawing ant then clip the region:
- (void)drawRect:(NSRect)dirtyRect
{
[NSGraphicsContext saveGraphicsState];
[[NSBezierPath bezierPathWithRoundedRect:dirtyRect xRadius:3.0 yRadius:3.0] setClip];
[super drawRect:dirtyRect];
[NSGraphicsContext restoreGraphicsState];
}
In general it is better to subclass NSTextFieldCell instead to make custom drawing, because cells are responsible for drawing.

For reference for future readers, this is probably how you should do it, by subclassing NSTextFieldCell:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
NSBezierPath *betterBounds = [NSBezierPath bezierPathWithRoundedRect:cellFrame xRadius:CORNER_RADIUS yRadius:CORNER_RADIUS];
[betterBounds addClip];
[super drawWithFrame:cellFrame inView:controlView];
if (self.isBezeled) { // optional, but provides an example of drawing a prettier border
[betterBounds setLineWidth:2];
[[NSColor colorWithCalibratedRed:0.510 green:0.643 blue:0.804 alpha:1] setStroke];
[betterBounds stroke];
}
}
I draw an additional bluish border here (though that seems to be unnecessary for your black box)

Related

Message flow problems with NSTableHeaderView and NSTableHeaderCell while trying to produce transparent NSTableView Header

Problem: I am trying to create a custom transparent TableView Header and I have created subclasses of NSTableHeaderView and NSTableHeaderCell and overridden -drawWithFrame:inView and -drawInteriorWithFrame:inView in the NSTableHeaderCell subclass. These methods are working as expected, but only when the table column header is first drawn. After the user clicks on the table header, however, it is re-drawn with a white background. To get specific, here are the custom method implementations:
#interface MYTableHeaderCell : NSTableHeaderCell
#end
#implementation MYTableHeaderCell
-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView{
//[super drawWithFrame:cellFrame inView:controlView];
NSBezierPath *path = [NSBezierPath bezierPathWithRect:cellFrame];
NSColor *clearColor = [NSColor clearColor];
[clearColor setFill];
[path fill];
[self drawInteriorWithFrame:cellFrame inView:controlView];
}
-(void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView{
//[super drawInteriorWithFrame:cellFrame inView:controlView];
NSBezierPath *path = [NSBezierPath bezierPathWithRect:cellFrame];
NSColor *clearColor = [NSColor clearColor];
[clearColor setFill];
[path fill];
NSRect titleRect = [self titleRectForBounds:cellFrame];
[self.attributedStringValue drawInRect:titleRect];
}
-(NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView{
return [NSColor clearColor];
}
-(BOOL)isOpaque{
return NO;
}
When the table view header is first drawn, it
has a transparent background as intended.
After clicking on the header, however, it is
redrawn to have a white background.
As far as I can tell, after clicking on the table view header
-drawInteriorWithFrame:inView:
is still called when the header needs to be drawn. However,
-drawWithFrame:inView:
is not. It also appears that another class is drawing a white view underneath the cell text.
I have looked through the NSTableHeaderCell and NSTableHeaderView class descriptions along with all of their superclasses but I can't figure out why the white background is being drawn. I'm obviously missing something fundamental.
Question: What is causing the white view to be drawn?
The
highlight(flag: Bool, withFrame cellFrame: NSRect, inView controlView: NSView)
method is not implemented. Override this method and copy your code from your drawRect method in there and this should be done.

How to disable the mouse hover expand effect on a NSTextView's NSScrollView scroller?

I have a NSTextView and here's the normal size of the scroller:
And here's what happens when I hover the scroller of the textview:
However, I don't want to have this 'expand' effect. How can I remove it? I've tried to search around on how to perform this, but I couldn't find anything. I just want to have the regular scroller size (the thinner one) all the time, even if the user hovers it. Is this possible?
Thanks
I recommend subclassing the NSScroller and override – drawArrow:highlight: / – drawKnobSlotInRect:highlight: / – drawKnob methods so you have a stable scroller appearance.
P.S. Don't forget to set your new scroller class in XIB-file for the scrollers.
UPDATE
Here is the sample code:
- (void)drawKnob
{
// call the default implementation for Overlay Scrollers
if (self.scrollerStyle == NSScrollerStyleOverlay)
{
[super drawKnob];
return;
}
if (_style == NSScrollerKnobStyleLight || _style == NSScrollerKnobStyleDefault)
[[NSColor colorWithCalibratedWhite:1.0 alpha:0.8] setFill];
else [[NSColor colorWithCalibratedWhite:0 alpha:0.4] setFill];
// Note: you can specify the rect with fixed width here
NSRect knobRect = [self rectForPart:NSScrollerKnob];
// VERTICAL SCROLLER
NSInteger fullWidth = knobRect.size.width;
knobRect.size.width = round(knobRect.size.width/2);
knobRect.origin.x += (NSInteger)((fullWidth - knobRect.size.width)/2);
// draw...
NSBezierPath * thePath = [NSBezierPath bezierPath];
[thePath appendBezierPathWithRoundedRect:knobRect xRadius:4 yRadius:4];
[thePath fill];
}
//---------------------------------------------------------------
- (void)drawKnobSlotInRect:(NSRect)slotRect highlight:(BOOL)flag
{
// call the default implementation for Overlay Scrollers
// draw nothing for usual
if (self.scrollerStyle == NSScrollerStyleOverlay)
{
[super drawKnobSlotInRect:slotRect highlight:flag];
}
}
//---------------------------------------------------------------
- (void)drawArrow:(NSScrollerArrow)whichArrow highlight:(BOOL)flag
{
// call the default implementation for Overlay Scrollers
// draw nothing for usual
if (self.scrollerStyle == NSScrollerStyleOverlay)
{
[super drawArrow:whichArrow highlight:flag];
}
}
I don't know what exact style you want, but this category might help you.
#implementation NSScrollView (SetScrollStyle)
- (void) setHidingScroll
{
[self setScrollerStyle:NSScrollerStyleOverlay];
[[self verticalScroller] setControlSize: NSSmallControlSize];
[[self verticalScroller] setKnobStyle:NSScrollerKnobStyleDark];
[self setScrollerKnobStyle:NSScrollerKnobStyleDark];
[[self verticalScroller] setScrollerStyle:NSScrollerStyleOverlay];
}
and usage
[scrollView setHidingScroll];

Rounded NSView in a Transparent Window

I'm trying to make a transparent NSWindow with a rounded view in there.
I'm trying to have a rounded view with a transparent window.
This is what it looks like now: (see the little dots in the corners)
Here's another example with the border radius set to 10px (set in NSView drawRect):
I am using code from this Apple sample: https://developer.apple.com/library/mac/#samplecode/RoundTransparentWindow/Introduction/Intro.html
Specifically this method in my NSWindow subclass:
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag {
// Using NSBorderlessWindowMask results in a window without a title bar.
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self != nil) {
// Start with no transparency for all drawing into the window
[self setAlphaValue:1.0];
// Turn off opacity so that the parts of the window that are not drawn into are transparent.
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
}
return self;
}
And this in my NSView subclass:
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] set];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[thePath appendBezierPathWithRoundedRect:dirtyRect xRadius:3 yRadius:3];
[thePath fill];
}
Can anyone tell me what I'm missing here?
Thanks.
Are you looking for something like the following, where there's a red outline (stroke), but the center area is transparent?
If so, to achieve that, I used the following code:
- (void)drawRect:(NSRect)frame {
frame = NSInsetRect(self.frame, 3.0, 3.0);
[NSBezierPath setDefaultLineWidth:6.0];
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:frame
xRadius:6.0 yRadius:6.0];
[[NSColor redColor] set];
[path stroke];
}
If that's what you're looking for, you can probably use that as a starting point. You'll want to make sure that you inset the frame rect one half of the stroke line width, so as to avoid the problem with clipping the corners like you were seeing.
Not sure if this is what you are looking for but there is a great class by Matt Gemmell called MAAttachedWindow and can be found here: http://mattgemmell.com/2007/10/03/maattachedwindow-nswindow-subclass/
It's a little older but still works great for me when I need to do a 'floating' popup window and configure transparency, border radii, and even add a small arrow for context if desired. I use it all the time.

Selected Menu Item Color not aligned?

I thinks a image is better than a thousand words.
What you see in this picture are NSBox Subclass in a NSViewCollection. I set the fill color to "Selected Menu Item Color" is Interface builder.
Why is the color like that ?
Edit : After reading this SO post, I found out I probably have to set the setPatternPhase. But how/where ?
The "Selected Menu Item Color" pattern is designed to be used on "menu-high" items, and it looks like your NSBox is taller than the pattern is.
That is, you could mess around with the pattern phase origin using code from my answer, but I don't think it will do you any good because the pattern just isn't tall enough for your NSBox.
Inspired by Smilin Brian's solution I came up with this :
-(void) drawRect: (NSRect)dirtyRect
{
[super drawRect:dirtyRect];
if(mouseOver) {
[NSGraphicsContext saveGraphicsState];
CGFloat yOffset = NSMaxY([self convertRect:self.bounds toView:nil]);
CGFloat xOffset = NSMinX([self convertRect:self.bounds toView:nil]);
[[NSGraphicsContext currentContext] setPatternPhase:NSMakePoint(xOffset, yOffset)];
[[NSColor selectedMenuItemColor ] setFill];
NSRectFill(dirtyRect);
[NSGraphicsContext restoreGraphicsState];
}
}
For the moment it just handles mouseOver events and not selection but it's pretty much the same.
Just for the who might need that for a NSBox I used :
- (void)awakeFromNib{
NSTrackingArea* trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds
options: (NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow )
owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
}
and
- (void)mouseEntered:(NSEvent *)theEvent {
mouseOver = true;
[self setNeedsDisplayInRect:self.bounds];
[self needsDisplay];
}
- (void)mouseExited:(NSEvent *)theEvent {
mouseOver= false;
[self setNeedsDisplayInRect:self.bounds];
[self needsDisplay];
}

Highlighting an Edited NSBrowserCell & Draw A Focus Ring on its NSText?

What I'm trying to do
I'm trying to add edit-in-place functionality to the Connection Kit's NSBrowser. I'd like this behaviour to be functionally and visually similar to Finder's implementation.
The visual effect I'm aiming for
What I've got so far
The arrows indicate focus ring & cell highlighting in Finder's implementation, and the lack of it in mine.
I have tried
Setting the background colour of the cell in the controller, in it's drawInteriorWithFrame method
The same for the field editor
setFocusRingType:NSFocusRingTypeDefault for the field editor & cell both in the controller & the draw method
Manually drawing the highlight color in the draw method
Various combinations of the above, and undoubtedly some I've forgotten.
The best I've managed was getting the area surrounding the cell's image coloured with the highlight colour.
Is there some fundamental that I'm missing here? Could someone please suggest a starting point for approaching this? Is drawInteriorWithFrame the place to be doing this?
I've got editing working fine - I'm just having trouble with the visual aspects.
Code to allow editing:
// In the main controller
int selectedColumn = [browser selectedColumn];
int selectedRow = [browser selectedRowInColumn:selectedColumn];
NSMatrix *theMatrix = [browser matrixInColumn:selectedColumn];
NSRect cellFrame = [theMatrix cellFrameAtRow:selectedRow column:0];
NSText *fieldEditor = [[browser window] fieldEditor:YES
forObject:editingCell];
[cell editWithFrame:cellFrame
inView:theMatrix
editor:fieldEditor
delegate:self
event:nil];
And in my subclass of NSBrowserCell:
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
image = [[self representedObject] iconWithSize:[self imageSize]];
[self setImage:image];
NSRect imageFrame, highlightRect, textFrame;
// Divide the cell into 2 parts, the image part (on the left) and the text part.
NSDivideRect(cellFrame, &imageFrame, &textFrame, ICON_INSET_HORIZ + ICON_TEXT_SPACING + [self imageSize].width, NSMinXEdge);
imageFrame.origin.x += ICON_INSET_HORIZ;
imageFrame.size = [self imageSize];
[super drawInteriorWithFrame:cellFrame inView:controlView];
}
- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent {
NSRect imageRect, textRect;
NSDivideRect(aRect , &imageRect, &textRect, 20, NSMinXEdge);
self.editing = YES;
[super editWithFrame: textRect inView: controlView editor:textObj delegate:anObject event:theEvent];
}
You have to draw the focus ring yourself.
Add the following in drawWithFrame in your NSBrowserCell subclass
[NSGraphicsContext saveGraphicsState];
[[controlView superview] lockFocus];
NSSetFocusRingStyle(NSFocusRingAbove);
[[NSBezierPath bezierPathWithRect:NSInsetRect(frame,-1,-1)] fill];
[[controlView superview] unlockFocus];
[NSGraphicsContext restoreGraphicsState];
Try subclassing field editor object and override drawRect function like this :
- (void)drawRect:(NSRect)rect {
[super drawRect:rect];
NSSetFocusRingStyle(NSFocusRingOnly);
NSRectFill([self bounds]);
}
Hope this helps.