Search code examples
swiftmacoscocoansstackview

Hide view item of NSStackView with animation


I working with swift 4 for macOS and I would like to hide an stack view item with animation.

I tried this:

class ViewController: NSViewController {

    @IBOutlet weak var box: NSBox!
    @IBOutlet weak var stack: NSStackView!
    var x = 0



    @IBAction func action(_ sender: Any) {
        if x == 0 {

            NSAnimationContext.runAnimationGroup({context in
                context.duration = 0.25
                context.allowsImplicitAnimation = true

                self.stack.arrangedSubviews.last!.isHidden = true
                self.view.layoutSubtreeIfNeeded()
                x = 1
            }, completionHandler: nil)

        } else {

            NSAnimationContext.runAnimationGroup({context in
                context.duration = 0.25
                context.allowsImplicitAnimation = true

                self.stack.arrangedSubviews.last!.isHidden = false
                self.view.layoutSubtreeIfNeeded()
                x = 0
            }, completionHandler: nil)

        }

    }
}

The result will be:

enter image description here

It works! But I am not happy with the animation style. My wish is:

  • I press the button, the red view will be smaller to the right side
  • I press the button, the red view will be larger to the left side.

Like a sidebar or if you have an splitview controller and you will do splitviewItem.animator().isCollapsed = true

this animation of show/hide is perfect. Is this wish possible?

UPDATE self.stack.arrangedSubviews.last!.animator().frame = NSZeroRect

enter image description here

UPDATE 2

self.stack.arrangedSubviews.last!.animator().frame = NSRect(x: self.stack.arrangedSubviews.last!.frame.origin.x, y: self.stack.arrangedSubviews.last!.frame.origin.y, width: 0, height: self.stack.arrangedSubviews.last!.frame.size.height)

enter image description here


Solution

  • I just create a simple testing code which can animate the red view, instead of using button, I just used touchup, please have a look at the code:

    class ViewController: NSViewController {
    
    let view1 = NSView()
    let view2 = NSView()
    let view3 = NSView()
    var x = 0
    
    var constraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Do any additional setup after loading the view.
        view1.wantsLayer = true
        view2.wantsLayer = true
        view3.wantsLayer = true
    
        view1.layer?.backgroundColor = NSColor.orange.cgColor
        view2.layer?.backgroundColor = NSColor.green.cgColor
        view3.layer?.backgroundColor = NSColor.red.cgColor
    
        view1.translatesAutoresizingMaskIntoConstraints = false
        view2.translatesAutoresizingMaskIntoConstraints = false
        view3.translatesAutoresizingMaskIntoConstraints = false
    
        self.view.addSubview(view1)
        self.view.addSubview(view2)
        self.view.addSubview(view3)
    
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view1]|", options: [], metrics: nil, views: ["view1": view1]))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view2]|", options: [], metrics: nil, views: ["view2": view2]))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view3]|", options: [], metrics: nil, views: ["view3": view3]))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view1(==view2)][view2(==view1)][view3]|", options: [], metrics: nil, views: ["view1": view1, "view2": view2, "view3": view3]))
    
        constraint = NSLayoutConstraint(item: view3, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100)
        self.view.addConstraint(constraint)
    }
    
    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
    
    override func mouseUp(with event: NSEvent) {
        if x == 0 {
    
            NSAnimationContext.runAnimationGroup({context in
                context.duration = 0.25
                context.allowsImplicitAnimation = true
    
                constraint.constant = 0
                self.view.layoutSubtreeIfNeeded()
                x = 1
            }, completionHandler: nil)
    
        } else {
    
            NSAnimationContext.runAnimationGroup({context in
                context.duration = 0.25
                context.allowsImplicitAnimation = true
    
                constraint.constant = 100
                self.view.layoutSubtreeIfNeeded()
                x = 0
            }, completionHandler: nil)
    
        }
    }
    }
    

    enter image description here