Animating NSTabView Views

1.2k views Asked by At

I am trying to animate each view of NSTabView with a slide in when the view is selected. I have this working in a fashion, but it only animates the first time i select a new tab view. After that i do not see an animation when switching tab views, although i can see the the function is fired every time.?

override func tabView(tabView: NSTabView, didSelectTabViewItem tabViewItem: NSTabViewItem) {
    tabViewItem.view!.setFrameOrigin(NSPoint(x: tabViewItem.view!.frame.origin.x + 300, y: tabViewItem.view!.frame.origin.y))
    tabViewItem.view!.animator().setFrameOrigin(NSPoint(x: tabViewItem.view!.frame.origin.x - 300, y: tabViewItem.view!.frame.origin.y))
    // i can see this fires every time i switch tab views but the animation only works the fist time
}
3

There are 3 answers

0
muharrem On

I have changed it a little to solve first time problem. Just define an integer to store previous tab index in your delegate.

- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem{

    CABasicAnimation *controlPosAnim = [CABasicAnimation animationWithKeyPath:@"position"];
    if (prevTabViewIndex>[tabView indexOfTabViewItem:tabViewItem]) {
        [controlPosAnim setFromValue:[NSValue valueWithPoint:CGPointMake(tabViewItem.view.frame.origin.x - 500,tabViewItem.view.frame.origin.y)]];

    } else {
        [controlPosAnim setFromValue:[NSValue valueWithPoint:CGPointMake(tabViewItem.view.frame.origin.x + 500,tabViewItem.view.frame.origin.y)]];
    }

    [controlPosAnim setToValue:[NSValue valueWithPoint:CGPointMake(tabViewItem.view.frame.origin.x,tabViewItem.view.frame.origin.y)]];
    [[tabViewItem.view layer] addAnimation:controlPosAnim forKey:@"controlViewPosition"];
    [tabViewItem.view setAnimations:[NSDictionary dictionaryWithObjectsAndKeys:controlPosAnim, @"frameOrigin", nil]];

    [tabViewItem.view.animator setFrameOrigin : CGPointMake(tabViewItem.view.frame.origin.x,tabViewItem.view.frame.origin.y)];

    prevTabViewIndex = [tabView indexOfTabViewItem:tabViewItem];
}
0
Pegolon On

You have to make sure, that the NSTabView items are layer backed, so once (eg. in windowDidLoad()) call this:

for tabViewItem in self.tabView.tabViewItems {
  if let tabViewItemView = tabViewItem.view {
    tabViewItemView.wantsLayer = true
  }
}

This is my updated version of muharrem's answer for Swift with the correct offset for the animation:

private var previousTabViewIndex: Int = 0

public func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
  guard let tabViewItem = tabViewItem,
        let tabViewItemView = tabViewItem.view,
        let tabViewItemLayer = tabViewItemView.layer else { return }

  let animation = CABasicAnimation(keyPath: #keyPath(CALayer.position))
  let tabViewIndex = tabView.indexOfTabViewItem(tabViewItem)
  let origin = CGPoint(x: tabViewItemView.frame.origin.x, y: tabViewItemView.frame.origin.y)
  var offsetX = self.tabView.frame.width
  if self.previousTabViewIndex > tabViewIndex {
    offsetX *= -1
  }

  animation.fromValue = NSValue(point: CGPoint(x: origin.x + offsetX, y: origin.y))
  animation.toValue = NSValue(point: origin)
  tabViewItemLayer.add(animation, forKey: "controlViewPosition")

  self.previousTabViewIndex = tabViewIndex
}
0
M Afham On

If you're using NSTabViewController. You only need to set transitionOptions inside viewDidLoad Method.

class TabVC: NSTabViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        /// When new tab will select, it will perform slide transition.
        self.transitionOptions = .slideRight
    }
    
}