everybody! I perform cocoa drawing in view and perform some animation (moving text label) over this view. During the animation content in my view breaks:
before animation:
after animation:
How to improve this?
Thanks!
I replace dirtyRect with self.bounds:
- (void)drawRect:(NSRect)dirtyRect {
NSRect mainFrame = dirtyRect; //// <--- replace dirtyRect with self.bounds
//....drawing code
}
Related
I am working on a Mac app. I am trying to do a simple animation which makes an NSButton move down. The animation works really nicely, but when i do it, the background colour of my NSButton disappears for some reason. Here is my code:
// Tell the view to create a backing layer.
additionButton.wantsLayer = YES;
// Set the layer redraw policy. This would be better done in
// the initialization method of a NSView subclass instead of here.
additionButton.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.duration = 1.0f;
additionButton.animator.frame = CGRectOffset(additionButton.frame, 0.0, -20.0);
//additionButton.frame = CGRectOffset(additionButton.frame, 0.0, -20.0);
} completionHandler:nil];
Button move down animation:
Button after move down animation:
Update 1
Just to make it clear, I am not using a background image in my buttons. I am using a background NSColor which I set in the viewDidLoad method like so:
[[additionButton cell] setBackgroundColor:[NSColor colorWithRed:(100/255.0) green:(43/255.0) blue:(22/255.0) alpha:1.0]];
I presume this is an AppKit bug. There are a couple of ways you can work around it.
Workaround 1:
Don't use layers. The button you're animating seems to be small, and you might be able to get away with using a non layer-backed animation and still have it look decent. The button will redraw during each step of the animation, but it will animate correctly. That means this is really all you have to do:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
additionButton.animator.frame = CGRectOffset(additionButton.frame, 0, -20);
} completionHandler:nil];
Workaround 2:
Set the background color on the layer.
additionButton.wantsLayer = YES;
additionButton.layer.backgroundColor = NSColor.redColor.CGColor;
additionButton.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
additionButton.animator.frame = CGRectOffset(additionButton.frame, 0, -20);
} completionHandler:nil];
Workaround 3:
Subclass NSButtonCell, and implement -drawBezelWithFrame:inView:, drawing your background color there. Keep in mind that the parent view containing the button should be layer-backed, otherwise the button will still redraw on every step.
Dear fellow Cocoa programmers,
What I'd like to accomplish:
I have a checkbox, a popUpButton(which is hidden) and a NSView on my canvas.
If myCheckbox is checked -> show the popUpButton and draw a line through bezierPath on the NSView.
if myCheckbox is UNchecked -> Hide the popUpButton again and "undraw" the path
The code:
- (IBAction)isChecked:(id)sender {
//if myChekcbox is checked, show the pop up button
if ([sender state]==NSOnState) {
NSLog(#"Checked");
[myPopUp setHidden:NO];
}
else
{
//if the checkbox is unchecked, hide the popupbutton
[myPopUp setHidden:YES];
NSLog(#"Unchecked");
}
//reload my drawrect method (reload the view)
[self setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)dirtyRect
{
//if the checkedbutton is checked, draw the line
if ([myCheckbox state]==NSOnState)
{
NSBezierPath *myPath = [NSBezierPath bezierPath];
[myPath moveToPoint:NSMakePoint(10, 20)];
[myPath lineToPoint:NSMakePoint(50, 20)];
[myPath setLineWidth:2];
[myPath stroke];
}
}
The problem:
if checked state = NSOnState the popUpButton is visible but the line just won't draw and I wonder why... I personally think it's a connection(s) problem.
I uploaded the project file (it's rather small-35kb) here:Drawing.zip
Globally:
I've read the NSView documentation and it's saying there is only one way to draw to a view and it's through the drawRect method. Is this actually true? Also is this a descent way to draw to a view? (if function in the view and setNeedsDisplay:YES in the method)
thanks in advance,
Ben
You will need to get an NSColor instance and then call setStroke on it to set a current stroke color. It does not know which color to use to stroke the path at the start of drawRect:, so you have to tell it.
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.
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];
}
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?