Snapkit: Layout and animation is broken

Created on 3 Oct 2016  路  5Comments  路  Source: SnapKit/SnapKit

New Issue Checklist

  • [X] I have looked at the Documentation
  • [X] I have read the F.A.Q.
  • [X] I have filled out this issue template.

    Issue Info

| Info | Value |
| --- | --- |
| Platform | ios |
| Platform Version | 10.0 |
| SnapKit Version | 3.0.1 |
| Integration Method | cocoapods |

Issue Description

| SnapKit version | Broken |
| --- | --- |
| 0.22.0 | false |
| 3.0.1 | true |

  1. self(UIView) not respect to bottom side of subtitleLabel.
  2. Insets not work at all
  3. Animation is broken!

| view | frame |
| --- | --- |
| titleLabel | (15.0, 45.0, 345.0, 86.5) |
| subtitleLabel | (15.0, 65.0, 345.0, 18.0) |
| self | (-187.5, -49.0, 375.0, 98.0) |

subtitleLabel.x must be 146.5 (20.0 + 5.0 + 20.0 + 86.5 + 15.0)
self.height must be 179.5 (146.5 + 18.0 + 15.0)
self.x must be -179.5
self.y must be -20

    func initialize() {
        self.snp.makeConstraints { (make) in
            let superview = self.superview!
            make.top.equalTo(superview.snp.top)
            make.left.equalTo(superview.snp.left)
            make.right.equalTo(superview.snp.right)
        }

        // Title Label
        let _titleLabel = UILabel(frame: CGRect.zero)
        _titleLabel.numberOfLines = 0
        _titleLabel.textColor = UIColor.white
        _titleLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
        self.addSubview(_titleLabel)
        _titleLabel.snp.makeConstraints { (make) in
            let superview = _titleLabel.superview!
            make.top.equalTo(superview.snp.top).inset(statusBarHeight + 5 + 20)
            make.left.equalTo(superview.snp.left).inset(15)
            make.right.equalTo(superview.snp.right).inset(15)
        }
        titleLabel = _titleLabel

        // Subtitle Label
        let _subtitleLabel = UILabel(frame: CGRect.zero)
        _subtitleLabel.numberOfLines = 0
        _subtitleLabel.textColor = UIColor.white
        _subtitleLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.subheadline)
        self.addSubview(_subtitleLabel)
        _subtitleLabel.snp.makeConstraints { (make) in
            let superview = _subtitleLabel.superview!
            make.top.equalTo(_titleLabel.snp.top).inset(20)
            make.left.equalTo(superview.snp.left).inset(15)
            make.right.equalTo(superview.snp.right).inset(15)
            make.bottom.equalTo(superview.snp.bottom).inset(15)
        }
        subtitleLabel = _subtitleLabel
    }

    func dismiss() {
        // Hide with animation
        UIView.animate(withDuration: 1.3, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1.0, options: [], animations: { [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.snp.updateConstraints({ (make) in
                let superview = strongSelf.superview!
                make.top.equalTo(superview.snp.top).inset(-strongSelf.frame.height)
                strongSelf.layoutIfNeeded()
            })
        }, completion: { [weak self] (finished) in
            guard let strongSelf = self else { return }
            strongSelf.removeFromSuperview()
        })
    }

    func show() {        
        // Initial hidden
        self.snp.updateConstraints { (make) in
            let superview = self.superview!
            make.top.equalTo(superview.snp.top).inset(-self.frame.height)
            layoutIfNeeded()
            // Output
            print(self.titleLabel.frame)
            print(self.subtitleLabel.frame)
            print(self.frame)
        }

        // Show with animation
        UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1.0, options: [], animations: { [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.snp.updateConstraints({ (make) in
                let superview = strongSelf.superview!
                make.top.equalTo(superview.snp.top).inset(-20)
                strongSelf.layoutIfNeeded()
            })
        }, completion: { [weak self] (finished) in
            // ...
        })

        // Dismiss after 2 seconds
        let delayTime = DispatchTime.now(withDelay: 2.0) // DispatchTime extension
        DispatchQueue.main.asyncAfter(deadline: delayTime) { [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.dismiss()
        }
    }
need info

Most helpful comment

@cooler333 sometimes the internals of iOS change a bit, as a rule of thumb you need to call layoutIfNeeded() on the closest common superview of the two views the constraint is from and to.

So for self -> self.label you need to call it on self.superview

All 5 comments

@cooler333 can you try to simplify your example a bit to confirm this?

Something like this on dismiss:

// Initial hidden
        self.snp.updateConstraints { (make) in
            let superview = self.superview!
            make.top.equalTo(superview.snp.top).offset(500)
        }
        UIView.animate(withDuration: 0.5) { self.layoutIfNeeded(); self.superview.layoutIfNeeded() }

And see if anything happens? It's hard to follow such a complex code example without the full source to diagnose.

First guess is you may need to call self.superview.layoutIfNeeded() as well as self.layoutIfNeeded().

Also if you call layoutIfNeeded() inside a SnapKit constraint maker closure UIKit will not yet have the newly created constraints, you need to call layoutIfNeeded AFTER the constraint maker closure is finished.

        layoutIfNeeded()
        superview?.layoutIfNeeded()

This helps! fixed it.

but in iOS 9 + swift 2.3 have different behaviour. very strange. Thank You!

@cooler333 sometimes the internals of iOS change a bit, as a rule of thumb you need to call layoutIfNeeded() on the closest common superview of the two views the constraint is from and to.

So for self -> self.label you need to call it on self.superview

SnapKitIssueExample.zip

  • self(UIView) not respect to bottom side of subtitleLabel.
  • Insets not work at all

Example project

recently upgraded to SnapKit 3, found updateConstraints doesn't work consistently: similar to @cooler333 show/hide animation case, but my issue is updating constraints seems to work only for the first time, then updating won't take any effect. same codebase upgrade from Swift 2, didn't change a line. now it's broken

Was this page helpful?
0 / 5 - 0 ratings

Related issues

danieleggert picture danieleggert  路  3Comments

lolgear picture lolgear  路  5Comments

NicholasTD07 picture NicholasTD07  路  8Comments

swiftli picture swiftli  路  9Comments

chengkaizone picture chengkaizone  路  3Comments