SkeletonView does not update with AutoLayout

Created on 12 Apr 2019  路  14Comments  路  Source: Juanpe/SkeletonView

What did you do?

Tried to use isSkeletonable on a view laid out by auto-layout

What did you expect to happen?

The view would render in the auto-layout calculated position with shimmer

What happened instead?

The view did not show up

OR

If I set a background color, it shows up as background color, but no shimmer

Steps to reproduce the behavior

100% reproducible

let fakeText = UIView()
view.addSubview(fakeText)
fakeText.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: fakeText, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: CGFloat(16)).isActive = true
NSLayoutConstraint(item: fakeText, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: CGFloat(16)).isActive = true
NSLayoutConstraint(item: fakeText, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: CGFloat(16)).isActive = true

// Trying to let autolayout determine the width the component does not render with a shimmer
// NSLayoutConstraint(item: fakeText, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: 1, constant: -CGFloat(16)).isActive = true

// Setting the width statically it will render
NSLayoutConstraint(item: fakeText, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: CGFloat(200)).isActive = true

// If I set a background color, the component will render in the expected position with red background in the width relationship case.
// This demonstrates it as not an auto-layout issue.
// In the static width case, setting background has no effect.
fakeText.backgroundColor = UIColor.red

fakeText.isSkeletonable = true

SkeletonView Environment

SkeletonView version: 1.4.2
Xcode version: 10.1
Swift version: 4.2

馃棏 given up

Most helpful comment

I am also facing this issue.

All 14 comments

Wasn't able to upload a PR, but this diff reproduces the issue with the sample project. I could not reproduce it using Autolayout from storyboard, only from programatic autolayout.

  1. app loads without label
  2. make a skeleton change
  3. self.view.hideSkeleton() is called
  4. the next show of the skeleton correctly renders the label

patch.txt

zipped a mov showing what I describe
bug.mov.zip

Modifying this code in ViewController worked around the issue. To my surprise it needed both to clear skeleton on didAppear and updateConstraints.

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    refreshSkeleton()
}

override func updateViewConstraints() {
    super.updateViewConstraints()
    refreshSkeleton()
}

This work around also succeeded inside of my app. Wary of going into the underlying nest of code blind to figure out the race condition causing this.

I'm having the same issue. Any result about it?

@bpollock-vida What does your refreshSkeleton look like?

@bpollock-vida What does your refreshSkeleton look like?

I think it's this code:
app loads without label
make a skeleton change
self.view.hideSkeleton() is called
the next show of the skeleton correctly renders the label

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

I am also facing this issue.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Has this been fixed yet?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Still waiting for fix

@whoyawn @JMCPH I found refreshSkeleton in our code:

    func refreshSkeleton() {
        removeSkeleton()

        let gradient = SkeletonGradient(baseColor: UIColor(hexString: "EEEEEE"), secondaryColor: UIColor(hexString: "F9F9F9"))
        showAnimatedGradientSkeleton(usingGradient: gradient)
    }

    func removeSkeleton() {
        hideSkeleton()
        stopSkeletonAnimation()

        recursiveSubview { (subview) in
            subview.removeSkeleton()
        }
    }

and the recursive stuff

    /// Apply a closure on all subviews using breadth first traversal
    func recursiveSubview( apply: (inout UIView) -> Void) {
        var queue = [UIView]()
        while queue.isNotEmpty {
            var focus = queue.removeFirst()
            apply(&focus)
            queue = queue + focus.subviews
        }
    }

    /// Apply a closure to all superviews of a view
    func recursiveSuperview( apply: (inout UIView) -> Void) {
        var parent: UIView? = superview
        while parent != nil {
            guard var focus = parent else { break }
            apply(&focus)
            parent = focus.superview
        }
    }

(recursiveSuperview is used by a Skeletonize() helper method)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eduardbosch picture eduardbosch  路  5Comments

Manikandan271192 picture Manikandan271192  路  4Comments

mohpor picture mohpor  路  6Comments

adelbios picture adelbios  路  6Comments

GauravTechbirds picture GauravTechbirds  路  4Comments