Magic effect

Have a day to return to the seat, zhang Huang confusedly of this year’s unripe colleague be like to see salvation same catch me past: “cang shu Cang Shu, bad, you see it so!!”

When I saw it, Cang Shu, who never said foul words, couldn’t help saying, “I… Hell, in all my years of iOS I’ve never seen anything like this.” Call the leader to look at it. The leader took it to play for a while, and then said: “Ha ha ha, feel really want to achieve this effect, it is not so easy…”

What is the bug that makes us all so upset? Check out the GIF below:

This square cell is an ordinary collectionView cell. It’s used in many places. It’s been used for more than a year, and it’s always looked like this, and there’s never been any problem. When the cell was about to leave the screen, it seemed reluctant to leave. It actually shrank itself…

Do you want to help me debug

Here is the code to reproduce the bug, which can be reproduced on the iPhone 7 iOS 11 emulator. In order to write only one file, I simplified the code to 60 lines:

import UIKit

final class TestCell: UICollectionViewCell {

  override init(frame: CGRect) {
    let imageView = UIImageView(frame: .zero)
    let metadataView = UIView(frame: .zero)

    super.init(frame: frame)

    imageView.backgroundColor = UIColor.red
    metadataView.backgroundColor = UIColor.green

    for view in [imageView, metadataView] {
      addSubview(view)
      view.translatesAutoresizingMaskIntoConstraints = false
      view.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true
      view.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor).isActive = true
    }

    imageView.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor).isActive = true
    imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true

    metadataView.topAnchor.constraint(equalTo: imageView.bottomAnchor).isActive = true
    metadataView.heightAnchor.constraint(equalToConstant: 25).isActive = true
    metadataView.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor).isActive = true} required public init? (coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented")
  }
}

final class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

  override func viewDidLoad() { super.viewDidLoad() self.collectionView! .contentInsetAdjustmentBehavior = .never self.collectionView! .register(TestCell.self,forCellWithReuseIdentifier: "Cell")
  }

  // MARK: UICollectionViewDataSource

  override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10
  }

  override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    return collectionView.dequeueReusableCell(withReuseIdentifier: "Cell".for: indexPath)
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let measurementCell = TestCell()
    letWidth = (collectionView bounds. Size. Width - 20) / 2.0 measurementCell widthAnchor. The constraint (equalToConstant: width).isActive =true
    return CGSize(width: width, height: measurementCell.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height)
  }
}
Copy the code

Constraints are written in the system’s native way. You may be unfamiliar with the native way when you use third-party libraries more often. For a quick explanation, let’s assume red is the picture and green is the description:

  1. The left, right, and top of the image constraints to the parent view, height = width
  2. Describe the left, right, and bottom constraints to the parent view, fixed height 25, top to bottom of the image

There it is. Can you see what the problem is?

A few guesses

Q: Is there something wrong with the layout? A: using the simplest UICollectionViewFlowLayout…… Didn’t override anything.

Q: Is constraint conflict? A: What do you think is the problem with my restraint? There won’t be any conflict.

Q: Is the Cell size wrong? A: The most common automatic calculation… Log it to see if it works. And even if something goes wrong, scrolling doesn’t calculate size in real time… It was rolling and shrinking…

Q: the leadingAnchor. The constraint (equalTo: Self. LayoutMarginsGuide. LeadingAnchor). IsActive = true. This self layoutMarginsGuide. LeadingAnchor is what the devil, Can’t you just use self. LeadingAnchor? A: You guessed it… To save the navigation trouble by changing self.LayoutMargins to The layoutMarginsGuide, there is no problem with the normal Self.LeadingAnchor.

A: Yes, it’s only for iOS 11… It can be reproduced on any phone, but it does have something to do with the iPhoneX…

Did the smart reader guess what the problem was? 🙂

It’s just a row missing

The solution to this problem is simply to add a sentence to the cell’s init method

self.insetsLayoutMarginsFromSafeArea = false
Copy the code

InsetsLayoutMarginsFromSafeArea this attribute for all UIView default to YES (I think it is not too science), when it is YES, the view of layoutMargins will adjust for differences in safeArea. This way, even with layoutMargins set to a fixed value like layoutMargins =.zero, the site will simply expand to the margins to avoid the iPhoneX’s bangs. In this way, the effect of the above magic bug is not strange.

Layout Margins benefits and pits

So, in fact, it should be a very common question, why do not often encounter it? I think it’s because we don’t have enough constraints on the layoutMarginsGuide.

LayoutMargins is a great way to change Insets. For example, if I wanted to create a generic application that would allow users to alter the Insets, if I didn’t have to resort to layoutMargins, I would want to maintain four constraints:

// properties
var leadingInsetConstraint: NSLayoutConstraint!
var trailingInsetConstraint: NSLayoutConstraint!
var topConstraint: NSLayoutConstraint!
var bottomConstraint: NSLayoutConstraint!

// during init
self.leadingInsetConstraint = someView.leadingAnchor.constraint(equalTo: self.leadingAnchor)
self.leadingInsetConstraint.isActive = true
self.trailingInsetConstraint = someView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
self.trailingInsetConstraint.isActive = true
self.topInsetConstraint = someView.topAnchor.constraint(equalTo: self.topAnchor)
self.topInsetConstraint.isActive = true
self.bottomInsetConstraint = someView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
self.bottomInsetConstraint.isActive = true/ / configuration of the self. LeadingInsetConstraint. Constant = an inset. Left / / assume that we do not consider the Arabic. Self trailingInsetConstraint. Constant  = inset.right self.topInsetConstraint.constant = inset.top self.bottomInsetConstraint.constant = inset.bottomCopy the code

If I were to use layoutMagins, the code would be much simpler, without storing a single attribute:

// during init
self.leadingInsetConstraint = someView.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor)
self.trailingInsetConstraint = someView.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor)
self.topInsetConstraint = someView.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor)
self.bottomInsetConstraint = someView.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor)

// configuration
self.layoutMargins = insets
Copy the code

If you’re using Direct Margins, even the Arabic TAB is automatically fixed.

But it also has some potholes, and the one mentioned above is one of them. I’ll list the other two randomly:

  1. The default value for layoutMargins is not zero. This makes me never understand apple’s brain circuitry, which defaults to UIEdgeInsets(8,8,8,8). Maybe 8 is some Apple engineer’s lucky number…
  2. Layout Navigation may not be able to use layout Navigation correctly without adding it to View Hierarchy. We’ve had a bug where addSubview and layoutMargins are set first, and the reverse navigation seems to have failed. Is the latest edition fixed?