facebookarchive / Shimmer

An easy way to add a simple, shimmering effect to any view in an iOS app.
Other
9.35k stars 1.11k forks source link

ShimmerView does NOT work with Constraints #68

Closed Brandon-T closed 7 years ago

Brandon-T commented 7 years ago

I created a UIView. Added it as contentView of the shimmerView.

shimmerView is constrained to my TableViewCell.contentView -- left, right, top, bottom. The UIView is then constrained to shimmerView -- left, right, top, bottom.

[shimmerView setShimmering:YES]; then gives the error:

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x170284150 V:|-(0)-[UIImageView:0x1008b6b90]   (active, names: '|':UITableViewCellContentView:0x1008b1f10 )>",
    "<NSLayoutConstraint:0x1702841f0 UIImageView:0x1008b6b90.height == 175   (active)>",
    "<NSLayoutConstraint:0x170284380 V:[UIImageView:0x1008b6b90]-(0)-[FBShimmeringView:0x1008b4de0]   (active)>",
    "<NSLayoutConstraint:0x1702843d0 FBShimmeringView:0x1008b4de0.bottom == UITableViewCellContentView:0x1008b1f10.bottom   (active)>",
    "<NSLayoutConstraint:0x17409ab80 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x1008b1f10.height == 43.5   (active)>"
)

If I go to FBShimmeringLayer layoutSubViews and comment out:

_contentLayer.anchorPoint = CGPointMake(0.5, 0.5);
  _contentLayer.bounds = r;
  _contentLayer.position = CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r));

the errors go away but Shimmering no longer works.

grp commented 7 years ago

You don't need to constrain the size of the content view: it is automatically sized to match the shimmer view, through the lines of code you showed above.

Does it work without that constraint?

Brandon-T commented 7 years ago

No it doesn't work without that constraint. Instead it doesn't display at all.

With FBShimmeringView: http://imgur.com/50W0YOc Without FBShimmeringView: http://imgur.com/v2hB2Qw

I created a sample test project to show the issue: https://github.com/Brandon-T/FBShimmerTest/tree/master/FacebookShimmerTest

In the LoadingCell class I added a macro #define kDisableFBShimmering. If it is defined, then you can see everything works perfectly. The views get displayed. Console does not log any errors.

However, when I comment it out (enable FBShimmering), you can see the console spitting out a lot of errors and nothing gets disabled on screen.

grp commented 7 years ago

I took a look in the Xcode view debugger, and it looked like the bottom view is positioned and sized correctly. The issue is the subviews of the bottom view. I remembered that autolayout requires geometry methods to be called on UIView, rather than CALayer, as Shimmer is currently implemented.

Changing that makes the views appear, but there's still log messages about unsatisfied constraints. Investigating those, it appears the issue is that your constraints setup in BottomView require a certain minimum width and height of the BottomView to resolve. In a pure-autolayout world, that's OK, because that requirement is factored into constraint resolution. But Shimmer's manual sizing of the BottomView happens after the autolayout pass, so the BottomView needs a sufficient initial size.

In summary, the two fixes to make this work:

Brandon-T commented 7 years ago

This works. However, you cannot use UITableViewAutomaticDimension with the above. For example, if you constrained [shimmerView.heightAnchor constraintGreaterThanOrEqualToConstant:50.0].active = YES and made the UITableView height dynamic, you will get more constraint problems. However if you did the same to bottomView, it will work just fine.

I will be using a static height anyway so it doesn't matter, but I just thought I'd mention it in case someone came along with the same issue.