Elbimio
Elbimio

Reputation: 1041

How to fade out a NSView with animation?

This is as simple as can be so I can't for the life of me find what's wrong, I looked through the documentation as a guide but it still didn't work. I have a view inside a larger view. An IBAction is supposed to fade out the inner view... that's it. Here's what I've got:

NSViewAnimation *theAnim;
NSMutableDictionary *viewDict;

// Create the attributes dictionary for the view.
viewDict = [NSMutableDictionary dictionaryWithCapacity:2];

// Set the target object to be the view.
[viewDict setObject:_innerView forKey:NSViewAnimationTargetKey];

// Set this view to fade out
[viewDict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];

theAnim = [[NSViewAnimation alloc] initWithViewAnimations:@[viewDict]];

// Set some additional attributes for the animation.
[theAnim setDuration:1.0];

// Run the animation.
[theAnim startAnimation];

I checked the viewDict and theAnim with NSLog and neither are nil. I pretty much copied this from an old program I had where this was working, can't find what's wrong now.

I'm using Xcode 5.1.1.

Upvotes: 33

Views: 19493

Answers (3)

Vlad
Vlad

Reputation: 6730

The Swift version which successfully working.

// Extension
extension NSViewAnimation {

   public static func make(target: NSView, effect: NSViewAnimation.EffectName) -> NSViewAnimation {
      var animationDict: [NSViewAnimation.Key : Any] = [:]
      animationDict[.target] = target
      animationDict[.effect] = effect
      let viewAnimation = NSViewAnimation(viewAnimations: [animationDict])
      return viewAnimation
   }
}

// Custom subclass of NSView
class MySimpleView: NSView {

   override func draw(_ dirtyRect: NSRect) {
      NSColor.brown.setFill()
      dirtyRect.fill()
   }

   func animateFadeOut() {
      let animation = NSViewAnimation.make(target: self, effect: .fadeOut)
      animation.start()
   }
}

// Usage
let containerView = NSView()
let view = MySimpleView(frame: CGRect(x: 20, y: 20, width: 60, height: 60))
view.autoresizingMask = []
containerView.addSubview(view)

// At some point later
view.animateFadeOut() // Works as expected.

Upvotes: 0

Paulo Mattos
Paulo Mattos

Reputation: 19349

For those looking for a Swift version instead:

NSAnimationContext.runAnimationGroup({ context in
    context.duration = 1
    self.view.animator().alphaValue = 0
}, completionHandler: {
    self.view.isHidden = true
    self.view.alphaValue = 1
})

For layer-backed views, this is enough:

view.animator().isHidden = true

For those already on Swift 5.3 or above, you can leverage the new Multiple Trailing Closures syntax sugar here for an even cleaner version:

NSAnimationContext.runAnimationGroup { context in
    context.duration = 1
    self.view.animator().alphaValue = 0
} completionHandler: {
    self.view.isHidden = true
    self.view.alphaValue = 1
}

Upvotes: 13

Ken Thomases
Ken Thomases

Reputation: 90681

The modern approach is much easier:

[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
    context.duration = 1;
    view.animator.alphaValue = 0;
}
completionHandler:^{
    view.hidden = YES;
    view.alphaValue = 1;
}];

If the view hierarchy is layer-backed, it's actually sufficient to do:

view.animator.hidden = YES;

Upvotes: 85

Related Questions