NSTableView with rounded corners - macos

I'm trying to customize the UI of my application and I want my NSTableView to have rounded corners. So I subclassed NSTableView and got this:
However, when I populate the table and select a row, the selection is drawn over the border like this:
I've tried adding a clip in the table view drawing code and it doesn't work. Any suggestions for how I can fix this?
Edit:
My drawing code in the NSTableView is the following:
- (void)drawRect:(NSRect)dirtyRect {
[NSGraphicsContext saveGraphicsState];
NSRect frame = NSMakeRect(0.0, 0.0, [self bounds].size.width, [self bounds].size.height-1.0);
[[NSBezierPath bezierPathWithRoundedRect:frame xRadius:3.6 yRadius:3.6] addClip];
[super drawRect:dirtyRect];
[NSGraphicsContext restoreGraphicsState];
}
The actual rounded frame is drawn in the NSScrollView drawRect method. The interesting thing is that this does clip the selection of the very first and very last rows:
But not when the table is scrolling:
So the question remains: how can I clip all drawing inside the rounded frame of the NSScrollView?

I found that you can call this on the container scroll view of the table view.
self.scrollView.wantsLayer = TRUE;
self.scrollView.layer.cornerRadius = 6;
That's all I needed and it works. No subclassing needed.

I was able to solve this pretty nicely using CALayer. After trying subclassing everything from NSScrollView to NSTableView to NSClipView, and still getting the rendering problems shown above, I finally simply added this code to the drawRect of the NSScrollView subclass:
if (!self.contentView.wantsLayer) {
[self.contentView setWantsLayer:YES];
[self.contentView.layer setCornerRadius:4.0f];
}
And then I draw the frame in the same drawRect method of the NSScrollView. It solves all the problems above.

Related

Subview of subclassed NSView re-positioned incorrectly after drawing a focus ring

I wanted to create a focus ring outside a subclassed NSView to identify selection. My reference comes from here: Link.
I followed the reference, overwrote the -drawRect method as:
#property (nonatomic) BOOL shouldDisplayFocus;
...
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
if (_shouldDisplayFocus)
{
[self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
}
[super drawRect:dirtyRect];
[[NSColor blackColor] set];
NSRectFill(dirtyRect);
if (_shouldDisplayFocus)
{
NSSetFocusRingStyle(NSFocusRingTypeExterior);
NSBezierPath *path = [NSBezierPath bezierPathWithRect:NSInsetRect([self bounds], -1.0, -1.0)];
[[NSColor blackColor] set];
[path stroke];
[NSGraphicsContext restoreGraphicsState];
}
}
And its -mouseDown: method also overwritten:
- (void)mouseDown:(NSEvent *)theEvent
{
[super mouseDown:theEvent];
if (_delegate && [_delegate respondsToSelector:#selector(mouseDownAtView:withEvent:)])
{
[_delegate mouseDownAtView:self withEvent:theEvent];
}
}
And after the view is clicked, its delegate would set/un-set the focus ring and which would make its -drawRect: called again.
It worked and generated the focus ring outside the view correctly. However, one problem occurred soon:
I had an image view inside the subclassed view. As the image view rectangle was auto-layout with NSLayoutConstraint objects, I create four NSLayoutConstraint outlets to adjust their values. I do not frequently change the layout constraints. Actually, as the image size remained unchanged, I would not set them.
Here is the situation when the subclassed view not clicked (seemed fine):
Then click on the image (the focus ring generated, but...):
And I tried resize the window, things got even more sadly "FUNNY":
I could not understand why the problem is or how to solve that. Could anyone help me with that? I have uploaded my sample code here: Download
Quite sad that no one answer this question.
I noticed that the subviews also layouted incorrectly when they were add to this view by -addSubview: and -setFrame method.
Really late answer, but here it is anyway: you didn't call [NSGraphicsContext saveGraphicsState] at the start of the if (_shouldDisplayFocus) { block.
You call [NSGraphicsContext restoreGraphicsState] to pop the graphics state off the stack, but you never put anything on the stack. Cocoa is using the graphics state stack to draw everything so you are popping off some unknown state that has something to do with the position of the image. If you want to add the focus ring style and be able to remove the focus ring style you need to first save the graphics state, set the focus ring style to whatever you want, and then restore the graphics state back to what it was.

Use NSScrollView together with Cocos2d-mac (NSOpenGLView)

Has anybody used a NSScrollView to control scrolling using the cocos2d-mac framework?
After much struggling I managed to make UIScrollView work with cocos2d-ios. Any pointers to using NSScrollView together with a NSOpenGLView would be appreciated.
I finally managed to get a NSScrollView working within my cocos2d-mac Xib.
The trick is that you have to programmatically overlay the OpenGLView over the NSScrollView main view (leaving room for the scroll bars) by first setting up a fake view as the scrollView's documentView, and then removing the openGLView from its parent view and adding it again (so the OpenGLView is drawn over the ScrollView). You can do it as follows:
appDelegate = [[NSApplication sharedApplication] delegate];
// Set up NSScrollView with dummy empty view as big as the layer
NSRect rect = NSMakeRect(0, 0, myLayer.width, myLayer.height);
NSView *view = [[NSView alloc] initWithFrame:rect];
appDelegate.scrollView.documentView = view;
[view release];
// Push OpenGLView back to front
[appDelegate.glView removeFromSuperview];
[appDelegate.splitLeftView addSubview:appDelegate.glView];
And then, you should use the scroll bars events to update the myLayer position accordingly.

cocoa -- getting nested NSViews and CALayers to resize proportionally

My NSWindow's contentView is an NSView subclass. It has some other NSView subclasses as subviews. The subviews are layer-based, and those layers in turn contain sublayers. Some of the sublayers have further sub-sublayers.
I want the whole thing to resize proportionally when the window is resized. What is the right way to set it up so that will happen?
Thanks
EDIT: I am not using Interface Builder at all.
Here's what I've done to get the contents of an NSView to scale proportionally as I resize the parent window. First, in interface builder, I added my NSView to the window, then added a reference to it in my AppDelegate. Mine happens to be called scrollView. I removed all of the auto-sizing behaviour from the scrollView.
Then, in my AppDelegate I added this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// keep the aspect ratio constant so that the content looks good
[window setContentAspectRatio:NSMakeSize(2, 1)];
window.delegate = self;
}
- (void)windowDidResize:(NSNotification *)notification {
// size the scrollView to fill the window, but keep its bounds constant
NSRect rect = [[window contentView] frame];
NSRect oldBounds = [scrollView bounds];
[scrollView setFrame:rect];
[scrollView setBounds:oldBounds];
}
This turns the AppDelegate into the window delegate too. Fine since I've not got much logic in it. By keeping the bounds constant while changing the frame, the contents of scrollView will be scaled down smoothly.

Core Animation with an NSView and subviews

I've subclassed NSView to create a 'container' view (which I've called TRTransitionView) which is being used to house two subviews. At the click of a button, I want to transition one subview out of the parent view and transition the other in, using the Core Animation transition type: kCATransitionPush. For the most part, I have this working as you'd expect (here's a basic test project I threw together).
The issue I'm seeing relates to resizing my window and then toggling between my two views. After resizing a window, my subviews will appear at seemingly random locations within my TRTransitionView. Additionally, it appears as if the TRTransitionView hasn't stretched correctly and is clipping the contents of its subviews. Ideally, I would like subviews anchored to the top-left of their parent view at all times, and to also grow to expand the size of the parent view.
The second issue relates to an NSTableView I've placed in my first subview. When my window is resized, and my TRTransitionView resizes to match its new dimensions, my TableView seems to resize its content quite awkwardly (the entire table seems to jolt around) and the newly expanded space that the table now occupies seems to 'flash' (as if in the process of being animated). Extremely difficult to describe, but is there any way to stop this?
Here's my TRTransitionView class:
-(void) awakeFromNib
{
[self setWantsLayer:YES];
[self addSubview:[self currentView]];
transition = [CATransition animation];
[transition setType:kCATransitionPush];
[transition setSubtype:kCATransitionFromLeft];
[self setAnimations: [NSDictionary dictionaryWithObject:transition forKey:#"subviews"]];
}
- (void)setCurrentView:(NSView*)newView
{
if (!currentView) {
currentView = newView;
return;
}
[[self animator] replaceSubview:currentView with:newView];
currentView = newView;
}
-(IBAction) switchToViewOne:(id)sender
{
[transition setSubtype:kCATransitionFromLeft];
[self setCurrentView:viewOne];
}
-(IBAction) switchToViewTwo:(id)sender
{
[transition setSubtype:kCATransitionFromRight];
[self setCurrentView:viewTwo];
}

NSTextView rounded border / stroke in Cocoa

I am subclassing NSTextView and over-riding the drawRect method in order to draw an NSBezierPathWithRoundedRect around the textview, however - as it has rounded edges, they interfere with the text in the text view.
Is there any way to apply some kind of margin or padding all around the text input area of the NSTextView so that it is more inset, and away from the rounded edges? Or is a better approach to sit the NSTextView within an NSView and apply the rounded stroke to the NSView instead?
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
[super drawRect:dirtyRect];
NSRect rect = [self bounds];
NSRect newRect = NSMakeRect(rect.origin.x+2, rect.origin.y+2, rect.size.width-3, rect.size.height-3);
NSBezierPath *textViewSurround = [NSBezierPath bezierPathWithRoundedRect:newRect xRadius:10 yRadius:10];
[textViewSurround setLineWidth:2.0];
[[NSColor whiteColor] set];
[textViewSurround stroke];
}
I think -setTextContainerInset: does what you need. Alternatively, you should not call super in your implementation of -drawRect: and draw the text container yourself.
You could try setting margins on the paragraph style of the whole textView's string.
Or maybe put your textview inside a larger parent view with rounded corners and a border?

Resources