NSWindow - flickering shadow during height animation (borderless, transparent)

  • Hallo all,

    I do use borderless & transparent NSWindow with shadow. The way I do initialize this window is at the end of this email. I experienced lot of problems with shadow, but found in Apple examples that the only way to fix shadow (= read to display it correctly) is to call …

    [self display];
    [self setHasShadow:NO];
    [self setHasShadow:YES];

    … I tried to use [self invalidateShadow], etc. but nothing did work. Shadow was always crippled (not following shape of the window, smaller height, …). Just these three lines do work. So far, so good. Whenever window frame changes, I do send these three message in my viewFrameDidChange: method. Works perfectly.

    But now, I do want to animate height of the window. I do use auto layout and I simply call …

    [self.heightConstraint.animator setConstant:newHeight];

    … in one of my subviews. Window is resizing correctly, stays where it should be, etc. But …

    - shadow is flickering,
    - first guess was it's because of setHasShadow:NO/YES in viewFrameDidChange: method,
    - when I comment out setHasShadow:NO/YES along with [self display], it's much more worse, because it's flickering and draws shadow artifacts around the window,

    My question is - how one can animate borderless/transparent window height with shadow, which is not flickering and is not crippled when view/window height did change? It drives me crazy. Searching on SO, searching in archives, nothing useful found yet.

    Regards,
    Robert

    P.S. Here's the link where you can see flickering shadow - http://d.pr/v/lbkQ

    - (id)initWithView:(NSView *)view anchorPoint:(NSPoint)anchorPoint position:(NSPoint)position distance:(CGFloat)distance {
      if ( !view ) {
        return nil;
      }

      NSSize size = view.intrinsicContentSize;
      NSRect contentRect = NSMakeRect( 0, 0, size.width, size.height );

      self = [super initWithContentRect:contentRect
                              styleMask:NSBorderlessWindowMask
                                backing:NSBackingStoreBuffered
                                  defer:NO];

      if ( !self ) {
        return nil;
      }

      _windowView = view;
      _anchorPoint = anchorPoint;
      _position = position;
      _distance = distance;

      [self setContentView:_windowView];

      [self setExcludedFromWindowsMenu:YES];
      [self setMovableByWindowBackground:NO];

      [self setOpaque:NO];
      [self setBackgroundColor:[NSColor clearColor]];

      [self setHasShadow:NO];
      [self useOptimizedDrawing:YES];

      [self setReleasedWhenClosed:NO];

      [self setFrame:[self windowRectWithSize:contentRect.size] display:YES];

      [self setAnchorAttribute:NSLayoutAttributeTop forOrientation:NSLayoutConstraintOrientationVertical];
      [self setAnchorAttribute:NSLayoutAttributeCenterX forOrientation:NSLayoutConstraintOrientationHorizontal];

      [[NSNotificationCenter defaultCenter] addObserver:self
                                              selector:@selector(viewFrameDidChange:)
                                                  name:NSViewFrameDidChangeNotification
                                                object:nil];

      return self;
    }
  • On Apr 23, 2013, at 5:55 AM, Robert Vojta wrote:

    > I do use borderless & transparent NSWindow with shadow. The way I do initialize this window is at the end of this email. I experienced lot of problems with shadow, but found in Apple examples that the only way to fix shadow (= read to display it correctly) is to call …
    >
    > [self display];
    > [self setHasShadow:NO];
    > [self setHasShadow:YES];
    >
    > … I tried to use [self invalidateShadow], etc. but nothing did work.

    It's not clear if you tried to use [self invalidateShadow] instead of all three lines or just instead of the last two.  The shadow shape is computed from the visible content of the window.  That's why there's a call to -display there, to make sure the visible content is up to date.  However, I would expect that using [self invalidateShadow] after a call to display would be at least as effective as toggling the hasShadow property and would possibly avoid the flicker.

    (For what it's worth, I think the examples that toggle hasShadow are simply holdovers from the days before -invalidateShadow was introduced.)

    In fact, you might just want to override -display in your window class and call -invalidateShadow after calling [super display].  That should keep the shadow always in sync with the contents.  If you do that, remove any explicit calls to -display that you're doing just to update the shadow as well as any other manipulation of the shadow.

    > - (id)initWithView:(NSView *)view anchorPoint:(NSPoint)anchorPoint position:(NSPoint)position distance:(CGFloat)distance {
    > ...
    > [self setHasShadow:NO];

    Why are you setting the window not to have a shadow here?

    Regards,
    Ken
  • On Wednesday, 24. April 2013 at 1:28, Ken Thomases wrote:

    Hi Ken,
    > It's not clear if you tried to use [self invalidateShadow] instead of all three lines or just instead of the last two. The shadow shape is computed from the visible content of the window. That's why there's a call to -display there, to make sure the visible content is up to date. However, I would expect that using [self invalidateShadow] after a call to display would be at least as effective as toggling the hasShadow property and would possibly avoid the flicker.

    yes, I tried to use [self invalidateShadow]. Flickering is less frequent sometimes, but it's still there. And what's much worse is that [self invalidateShadow] produces ugly artifacts sometimes. Shadow rectangle at the top with height of +-20px, etc. In my case, I have to set it to NO/YES to have 100% correct shadow.
    > Why are you setting the window not to have a shadow here?

    Yeah, you're right. It was copy & paste of code after experiments. Yes, I've got setHasShadow:YES there.

    Regards,
    Robert

    P.S. Anyone does know why the shadow is not drawn around subview where I have wantsLayer = YES? See this … http://d.pr/i/q71h The structure is …

    window (NSView)
    + contentView (NSView)
      - status view (NSView)
      - user selection view (NSView with wantsLayer = YES => no shadow)
      - photos view (NSView)
      - ...

    … I do not need layer for this view and actually no view has wantsLayer set to YES, asking just because I'm curious why the shadow is not drawn at all in this case.
  • Okay, so, here's the solution … The problem was in this …

    Wrong way

    I was listening for contentView frame changes (NSViewDidChangeFrameNotification) and I was calling display/invalidateShadow whenever I receieved this notification.

    Correct way

    NSWindowDelegate has windowDidResize notification. I moved display/invalidateShadow there and it stops flickering and works perfectly now.
previous month april 2013 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          
Go to today