0

I want to animate hide show when one of my view is hidden. so I'm using content hugging priority to animate that, but it failed it has a gap between view. here I show you the ui and my code

gap

This is 3 uiview code like the picture above

    scrollView.addSubview(chooseScheduleDropDown)
        chooseScheduleDropDown.translatesAutoresizingMaskIntoConstraints = false
        chooseScheduleDropDown.setContentCompressionResistancePriority(.required, for: .vertical)
        NSLayoutConstraint.activate([
            chooseScheduleDropDown.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            chooseScheduleDropDown.topAnchor.constraint(equalTo: scrollView.topAnchor),
            chooseScheduleDropDown.widthAnchor.constraint(equalToConstant: 285),
            chooseScheduleDropDown.heightAnchor.constraint(equalToConstant: 60)
        ])

        scrollView.addSubview(entryView)
        entryView.isHidden = true
        entryView.setContentHuggingPriority(.defaultLow, for: .vertical)
        entryView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            entryView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            entryView.topAnchor.constraint(equalTo: chooseScheduleDropDown.bottomAnchor, constant: topPadding),
            entryView.widthAnchor.constraint(equalToConstant: 285),
            entryView.heightAnchor.constraint(equalToConstant: 60)
        ])

        scrollView.addSubview(chooseDateView)
        chooseDateView.setContentHuggingPriority(.defaultLow, for: .vertical)
        chooseDateView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            chooseDateView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            chooseDateView.topAnchor.constraint(equalTo: entryView.bottomAnchor, constant: topPadding),
            chooseDateView.widthAnchor.constraint(equalToConstant: 285),
            chooseDateView.heightAnchor.constraint(equalToConstant: 60)
        ])
ferryawijayanto
  • 569
  • 4
  • 18
  • using hugging/compression resistance for animation is not a good idea. Instead, make it required and just add a `height = 0` constraint when you want to hide it. There is no combination of hugging and compression resistance that says some view should have zero height. – Sulthan Mar 23 '20 at 13:55
  • how can i do that @Sulthan – ferryawijayanto Mar 23 '20 at 14:04
  • 1
    If you embed your views in a `UIStackView`, managing the space between them when hiding / showing individual subviews is handled automatically. – DonMag Mar 23 '20 at 14:20
  • I was thinking the same thing at first, but my problem is when I want to set height inside uistackview. since there is 2 view have a different height. any idea @DonMag – ferryawijayanto Mar 23 '20 at 14:41
  • @ferryawijayanto - if constraints are setup correctly, there are no issues with setting / changing heights of elements in stack views. – DonMag Mar 23 '20 at 14:50
  • in interface builder I know how to do that, since all my UI create in code. I don't know how to translate it in code @DonMag – ferryawijayanto Mar 23 '20 at 15:10
  • @ferryawijayanto - I'm assuming when you tap a "row" (or just the "down arrow"), you are showing another view below that (a "list" of some sort)? If so, do you show it as another subview? Or do you change the height of that view (`chooseScheduleDropDown` for example) which reveals additional UI elements? And if you tap `chooseScheduleDropDown` to show its "list" should that *push down* the other two rows or cover them? – DonMag Mar 23 '20 at 15:18
  • chooseScheduleDropDown is a UIButton, when it tap. it show a 2 list of title selection which is a uitableview. when it choose the second one from list, it hide the uiview not push down another view actually I want to cover over the view below, and I still can't solve it too. when it choose the first selection it show the the uiview. @DonMag – ferryawijayanto Mar 23 '20 at 15:30

2 Answers2

1

Do

// declare an instance property 
var hCon:NSLayoutConstraint! 

// get only the height constraint out of the activate block
hCon = entryView.heightAnchor.constraint(equalToConstant: 60) 
hCon.isActive = true

and play with

hCon.constant = 300 / 0
view.layoutIfNeeded()
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
1

After exchanging comments, you have a number of different tasks to work on.

But, to give you an example of one approach to showing / hiding the "middle" view and having the bottom view move up / down, here is something to try. It will look like this:

enter image description here

Tapping the top (red) view will hide the middle (green) view and slide the bottom (blue) view up. Tapping the top (red) view again will slide the bottom (blue) view down and show the middle (green) view.

This is done by creating two top constraints for the Bottom view. One relative to the bottom of the Top view, and the other relative to the bottom of the Middle view, with different .priority values.

The example code is fairly straight-forward, and the comments should make things clear. All done via code - no @IBOutlet or @IBAction connections - so just create a new view controller and assign its custom class to AnimTestViewController:

class DropDownView: UIView {

}

class AnimTestViewController: UIViewController {

    let scrollView: UIScrollView = {
        let v = UIScrollView()
        return v
    }()

    let chooseScheduleDropDown: DropDownView = {
        let v = DropDownView()
        return v
    }()

    let entryView: DropDownView = {
        let v = DropDownView()
        return v
    }()

    let chooseDateView: DropDownView = {
        let v = DropDownView()
        return v
    }()

    var visibleConstraint: NSLayoutConstraint = NSLayoutConstraint()
    var hiddenConstraint: NSLayoutConstraint = NSLayoutConstraint()

    override func viewDidLoad() {
        super.viewDidLoad()

        [chooseScheduleDropDown, entryView, chooseDateView].forEach {
            v in
            v.translatesAutoresizingMaskIntoConstraints = false
            scrollView.addSubview(v)
        }

        scrollView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(scrollView)

        let g = view.safeAreaLayoutGuide

        let topPadding: CGFloat = 20.0

        // chooseDateView top anchor when entryView is visible
        visibleConstraint = chooseDateView.topAnchor.constraint(equalTo: entryView.bottomAnchor, constant: topPadding)

        // chooseDateView top anchor when entryView is hidden
        hiddenConstraint = chooseDateView.topAnchor.constraint(equalTo: chooseScheduleDropDown.bottomAnchor, constant: topPadding)

        // we will start with entryView visible
        visibleConstraint.priority = .defaultHigh
        hiddenConstraint.priority = .defaultLow

        NSLayoutConstraint.activate([

            scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
            scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),

            chooseScheduleDropDown.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            chooseScheduleDropDown.topAnchor.constraint(equalTo: scrollView.topAnchor),
            chooseScheduleDropDown.widthAnchor.constraint(equalToConstant: 285),
            chooseScheduleDropDown.heightAnchor.constraint(equalToConstant: 60),

            entryView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            entryView.topAnchor.constraint(equalTo: chooseScheduleDropDown.bottomAnchor, constant: topPadding),
            entryView.widthAnchor.constraint(equalToConstant: 285),
            entryView.heightAnchor.constraint(equalToConstant: 60),

            chooseDateView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),

            //chooseDateView.topAnchor.constraint(equalTo: entryView.bottomAnchor, constant: topPadding),
            visibleConstraint,
            hiddenConstraint,

            chooseDateView.widthAnchor.constraint(equalToConstant: 285),
            chooseDateView.heightAnchor.constraint(equalToConstant: 60),

        ])

        //entryView.isHidden = true

        chooseScheduleDropDown.backgroundColor = .red
        entryView.backgroundColor = .green
        chooseDateView.backgroundColor = .blue

        let tap = UITapGestureRecognizer(target: self, action: #selector(toggleEntryView(_:)))
        chooseScheduleDropDown.addGestureRecognizer(tap)

    }

    @objc func toggleEntryView(_ sender: UITapGestureRecognizer) -> Void {
        print("tapped")

        // if entryView IS hidden we want to
        //  un-hide entryView
        //  animate alpha to 1.0
        //  animate chooseDateView down

        // if entryView is NOT hidden we want to
        //  animate alpha to 0.0
        //  animate chooseDateView up
        //  hide entryView when animation is finished

        let animSpeed = 0.5

        if entryView.isHidden {

            entryView.isHidden = false
            hiddenConstraint.priority = .defaultLow
            visibleConstraint.priority = .defaultHigh

            UIView.animate(withDuration: animSpeed, animations: {
                self.entryView.alpha = 1.0
                self.view.layoutIfNeeded()
            }, completion: { _ in
            })

        } else {

            visibleConstraint.priority = .defaultLow
            hiddenConstraint.priority = .defaultHigh

            UIView.animate(withDuration: animSpeed, animations: {
                self.entryView.alpha = 0.0
                self.view.layoutIfNeeded()
            }, completion: { _ in
                self.entryView.isHidden = true
            })

        }

    }

}
DonMag
  • 69,424
  • 5
  • 50
  • 86