2

I'm trying to have a simple slide-down-from-top animation using auto layout (with the help of SnapKit).

Below is the code I use to generate a view, set its constraints and force a reload (thus display) of said view. In the same code cope I change the top constraint to a different value and call layoutIfNeeded in an animation block.

What happens instead though is that the view is displayed at the intended position without any animations whatsoever.

Here is the code called in a method after a short delay (for other purposes):

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
    self.view.addSubview(view)
    view.backgroundColor = .black

    view.snp.makeConstraints { (make) -> Void in
        make.left.right.equalToSuperview()
        make.height.equalTo(200)
        make.bottom.equalTo(self.view.snp.top) // Pin view to top to prepare slide-down animation
    }

    view.setNeedsLayout()
    view.layoutIfNeeded()

    view.snp.updateConstraints { (make) -> Void in
        make.bottom.equalTo(self.view.snp.top).offset(200)
    }

    view.setNeedsLayout()

    UIView.animate(withDuration: 5, animations: {
        view.layoutIfNeeded()
    })
})
  • I see several potential reasons for that. (1) It looks like you are trying to do a UI update in a separate thread. I'm surprised it's even working. That's a major no-no if you are coding things that way. (2) Why are you calling *setNeedsLayout()* and *layoutIfNeeded()* **outside** the *UIView.animate*? This is a likely culprit. (3) Constraint changes. Maybe this is something related to SnapKit, but calling *updateContraints()* isn't something I do when trying to change and then animate constraints. I change the constants/multipliers within them. –  Mar 19 '17 at 22:03
  • (1) Haven't thought after that. Thanks. Any tips how I should call a method after a certain delay to do UI updates? `Sleep()` obviously is a bad way as well, since it's blocking. (2) I need the view to be set up before I can animate it. (3)UpdateConstraint of SnapKit does just that. I compares if you have a given constraint and just updates the constant/multiplier. – Jason McKenzy Mar 19 '17 at 22:27
  • @dfd `DispatchQueue.main` is indeed using the `main thread`, so no worries there (source: https://www.hackingwithswift.com/read/9/4/back-to-the-main-thread-dispatchqueuemain). – Jason McKenzy Mar 19 '17 at 22:49
  • This question title is misleading. It seems it is more about constraints and animations. layoutIfNeeded() has zero to do with whether a view is on screen or not. The rendering system will put it on screen if it is not busy with a long running process but again it guarantees nothing as far as being rendered. – agibson007 Mar 20 '17 at 00:08

1 Answers1

2

Thanks to In Swift, how do I animate a UIView with auto-layout like a page sliding in? I found the culprit.

Although I don't 100% understand why this is, but layoutIfNeeded needs to be called from the superview, not the view itself.

So calling self.view.layoutIfNeeded() is the correct way. Note that view is the currently being added UIView, whereas self.view is the superview, where view is being added to.

Community
  • 1
  • 1
  • Using both `self.view` and `view` in the same section of code with those names referring to two different objects sounds very confusing. Did everything start working after your answer? If so, you can accept your answer so you get points for it. :) – Dave Weston Mar 19 '17 at 22:33
  • Yeah, the naming was not very smart :-) It did indeed solve the issue. Can only accept my own answer in 2 days though. – Jason McKenzy Mar 19 '17 at 22:47
  • 1
    Ah, ok. Glad you figured it out! – Dave Weston Mar 19 '17 at 22:49