NSScollView with flipped documentView is causing strange animation issues - macos

I'm experience the strangest thing. I'm working on a project that I started noticing this issue, so I created a sandboxed test to simplify the problem and see if I can figure it out.
I have document based application with a single window controller. inside that are these objects:
Subclassed NSScrollView with isFlipped=YES
Subclassed NSView with isFlipped=YES, this is the documentView in the above scrollview.
An NSImage in the document view
Then I try and fade the image to alpha 0 like this:
- (void)windowDidLoad {
[super windowDidLoad];
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(test) userInfo:nil repeats:FALSE];
}
- (void) test {
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:4];
self.errorOutline.animator.alphaValue = 0;
[NSAnimationContext endGrouping];
}
This is how it looks in Xcode:
This is what happens when it starts animating:
I've noticed that if I resize the window continually while it's animating, I get a ghosting image like effect where I an see the rest of the image.
Another strange issue is that if I set isFlipped=NO, then the issue doesn't happen. Which is not an option - the whole reason I'm flipping the view is so it's easier to manage adding cells to it without calculating positions and heights backwards.
Update: I've filed a radar bug as this is a really strange issue. http://openradar.appspot.com/20680289

Related

Magnify NSScrollView at cursor location

I have an NSScrollView with a custom NSImageView as its document view set up in the xib file. My problem comes when I want to magnify the NSScrollView. I would like it to behave like the Preview app on Mac, and I can get it to magnify just fine, but I want it to magnify with the cursor as the centered point.
I have this code in my custom NSScrollView class:
//Informs the receiver that the user has begun a pinch gesture.
- (void)magnifyWithEvent:(NSEvent *)event {
NSPoint conViewPoint =[[self contentView] convertPoint:[event locationInWindow] fromView:nil];
[self setMagnification:[event magnification]+[self magnification] centeredAtPoint:conViewPoint];
}
This works, however it also causes the scroll view to scroll down as it magnifies (though it doesn't move sideways at all). It's more noticeable when zooming in and out multiple times without lifting from the trackpad.
A similar problem seems to be in this question, however I'm using a pinch gesture and not buttons. I've been trying to offset the y axis scrolling as suggested for that question with
[[self documentView] scrollPoint:scrollPoint]
but I haven't been able to figure out a working formula for scrollPoint that accounts for the changing content view bounds to keep the point under the cursor in the same place. So, I'm wondering if there is a more proper approach to this or if the scrolling really is needed and I just need to figure out the math.
Thanks for any help with this
EDIT:
So I finally figured out the scrolling math. It took a while and way more complicated attempts, but is pretty simple in the end:
- (void)magnifyWithEvent:(NSEvent *)event {
NSPoint docViewPoint =[[self documentView] convertPoint:[event locationInWindow] fromView:nil];
NSPoint docRectOri=[self documentVisibleRect].origin;
float widthGapSize=-docRectOri.x;
float heightGapSize=-docRectOri.y;
if([event phase]==NSEventPhaseBegan){
startDocPoint=docViewPoint;
startPoint.x=(docViewPoint.x+widthGapSize)*[self magnification];
startPoint.y=(docViewPoint.y+heightGapSize)*[self magnification];
}
scrollPoint.x=startDocPoint.x-(startPoint.x/[self magnification]);
scrollPoint.y=startDocPoint.y-(startPoint.y/[self magnification]);
[self setMagnification:[event magnification]+[self magnification] centeredAtPoint:docViewPoint];
[[self documentView] scrollPoint:scrollPoint];
}
I still don't understand why setMagnification:centeredAtPoint: doesn't just work and am still wondering if anyone has some input on this as my workaround is very jittery on magnification because of the scrolling.

NSAnimationContext misbehaving

I have a button that triggers the main window to animate it's height thus displaying some options in a pull-down-menu. The button (and it's sibling) are placed in the two bottom corners of the window, so i want them to animate down with the window - being "locked" in a fixed distance from the bottom window edge.
Problem: The buttons animate properly maybe 1 out of 10 times - the rest of the time they get offset seemingly randomly on the y-axis. On top of that the two buttons get different offsets - they don't behave identical, although the animation code is the same.
Deployed and run on OSX 10.9 Mavericks the animation works properly - no misbehaving there.
Any ideas on how to get the following, simple code to work on OSX < 10.9?
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.2f];
[[[self window] animator] setFrame:NSMakeRect([self window].frame.origin.x, [self window].frame.origin.y-EXTRA_HEIGHT, ORIGINAL_WIDTH, ORIGINAL_HEIGHT+EXTRA_HEIGHT) display:YES];
[[_buttonOne animator] setFrameOrigin:NSMakePoint([_buttonOne frame].origin.x, CONSTANT_NUMBER)];
[[_buttonTwo animator] setFrameOrigin:NSMakePoint([_buttonTwo frame].origin.x, CONSTANT_NUMBER)];
[NSAnimationContext endGrouping];

Setting up Continuous rendering in a mac osx cocoa application using OpenGL

I'm starting to work on a 3D particle system editor and evolver. Ive done something similar in the past with OpenGL but this time I'm making a mac os x cocoa application. I just have a few questions regarding some code I keep running into on setting up OpenGL.
1) Why do I see a lot of people on the web using...
[self setNeedsDisplay:YES];
Is this the proper way to get OpenGL to render, I now understand it leads to drawRect being called, but is it the correct way?
2) Is drawRect the proper method I should be overriding for my render frame method?
Heres the code that I continue to run into on the web:
-(void) prepareOpenGL {
[[self window] makeFirstResponder:self];
glClearColor(1.0f, 1.0f, 0.0f, 10.f);
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0/60.0 target:self selector:#selector(idle:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
-(void)idle:(NSTimer *)timer {
if(![[NSApplication sharedApplication] isHidden])
[self setNeedsDisplay:YES];
}
-(void) drawRect:(NSRect)dirtyRect {
glClear(GL_COLOR_BUFFER_BIT);
}
You haven't indicated whether you will be drawing your OpenGL content within an NSOpenGLView or a CAOpenGLLayer. These two have slightly different ways of updating their content for display to the screen.
For an NSOpenGLView, you don't need to update the view within it's -drawRect: method. In fact, I think you won't want to trigger -setNeedsDisplay: to do a refresh of the NSView because of some overhead that might incur. In one of my applications, I use a CVDisplayLink to trigger updates at 60 FPS within my own custom rendering methods in an NSOpenGLView. None of these touch -drawRect:. Frames are presented to the screen upon calling [[self openGLContext] flushBuffer], not by forcing a redraw of the NSView.
CAOpenGLLayers are a little different, in that you override - drawInCGLContext:pixelFormat:forLayerTime:displayTime: with your custom rendering code. This method is triggered in response to a manual -setNeedsDisplay or by the CAOpenGLLayer itself if its asynchronous property is set to YES. It knows when it's ready to present new content by the boolean value you provide in response to -canDrawInCGLContext:pixelFormat:forLayerTime:displayTime:.
I've used both of these, and each has its advantages. CAOpenGLLayers make it much easier to overlay other UI elements on your OpenGL rendering, but their rendering methods can be difficult to get to work correctly from a background thread. NSOpenGLViews can be updated easily on a background thread using a CVDisplayLink, but are a bear to overlay content on.

Gray border when using NSBorderlessWindowMask

Whenever I try to create a custom window using NSBorderlessWindowMask and set an NSView (for example an NSImageView) as its contentView, I get a 1px gray border around the NSView and I don't seem to be able to get rid of it.
I have followed several approaches including Apple's RoundTransparentWindow sample code as well as several suggestions on StackOverflow.
I suspect the gray border is either coming from the window itself or the NSView.
Have any of you experienced this problem or do you have a possible solution?
The code is fairly straightforward. This is the init method of the custom window:
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
if (self != nil) {
[self setAlphaValue:1.0];
[self setBackgroundColor:[NSColor clearColor]];
[self setOpaque:NO];
}
return self;
}
To test this, in IB I place an NSImageView in that custom window WITHOUT border and yet the image in the NSImageView has a border. The same goes for other NSView subclasses, such as NSTextField, NSTableView.
In addition, I also noticed that the same is happening with the sample application (RoundTransparentWindow) of Apple. Is it even possible to draw an NSView in a custom window without a 1px border?
Thanks
Are you sure this happens when you use a regular NSView with no drawing? I bet not. Other controls (like NSImageView)have borders. Maybe you should double check to make sure they're turned off whe possible.
Update - How do you get your view into your window? You don't include that code. I created a basic test project (download it here) with an image well and it works just fine. See for yourself.

NSTextView not refreshed properly on scrolling

I have a NSTextView with a sizeable quantity of text. Whenever I scroll however, the view isn't updated properly. There are some artifacts that remain at the top or the bottom of the view. It appears that the view doesn't refresh itself often enough. If I scroll very slowly the view updates correctly though. If I add a border to the view everything works perfectly, borderless view is the one that has a problem. Here's a link to a screenshot:
Thanks
Have you set the setDrawsBackground and copiesOnScroll propertes for either the NSScrollView or the NSClipView?
The first thing I would suggest is turning off the "draws background" property of the NSScrollView:
[myScrollView setDrawsBackground:NO];
Note that this should be set on the NSScrollView, and not on the embedded NSClipView.
The following excerpt from the documentation may be relevant:
If your NSScrollView encloses an NSClipView sending a setDrawsBackground: message with a parameter of NO to the NSScrollView has the added effect of sending the NSClipView a setCopiesOnScroll: message with a parameter of NO. The side effect of sending the setDrawsBackground: message directly to the NSClipView instead would be the appearance of “trails” (vestiges of previous drawing) in the document view as it is scrolled.
Looks like the text field isn't even in the scrolling-area... Are you sure something isnt overlapping it?
I had a similar trouble - artifacts develop when the NSTextView is embedded in another scrollview (ie. a NSTableView).
I actually turned on the setdrawsbackground, and then added a nice color to make it disappear again.
-(void)awakeFromNib{
NSScrollView *scroll = [self enclosingScrollView];
[scroll setBorderType:NSNoBorder];
[scroll setDrawsBackground:YES];
[scroll setBackgroundColor:[NSColor windowBackgroundColor]];
}
This in combination with a scrollWheel event let me use the NSTextView in a NSTableView.
-(void)scrollWheel:(NSEvent *)theEvent{
NSScrollView *scroll = [self enclosingScrollView];
[[scroll superview] scrollWheel:theEvent];
}
I had the same trouble some time ago. I don't remember how I solved it.
Try to place the NSTextView to another view if the superview is a custom view. Just to see what will happen.

Resources