12

I am using UIStackView to layout UILabels in my UICollectionViewCell subclass. I'm using the iOS SDK 9.2

The scrolling of the collection view is smooth if I don't update the labels' text when I dequeue them. However, if I update their text as I dequeue them, the scrolling is very slow.

I made a very small demo to show the problem, to be ran on a device (not the simulator). You can create a new empty project and replace the contents of ViewController.swift with this:

import UIKit

class ViewController: UIViewController {

    override func loadView() {
        view = UIView()

        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: 100, height: 200)
        let collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: layout)
        collectionView.registerClass(Cell.self, forCellWithReuseIdentifier: "Cell")
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.dataSource = self
        view.addSubview(collectionView)

        let constraints = ["H:|-[collectionView]-|",
            "V:|[collectionView]|"
        ].flatMap { NSLayoutConstraint.constraintsWithVisualFormat($0, options: [], metrics: nil, views: ["collectionView": collectionView])
        }
        NSLayoutConstraint.activateConstraints(constraints)

    }
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! Cell

        //comment out the line below to make the scrolling smoother: 
        cell.fillLabels()

        return cell
    }
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 100
    }
}

class Cell: UICollectionViewCell {

    var labelArray = [UILabel]()

    func fillLabels() {
        for label in labelArray {
            label.text = "\(label.text!) yo"
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        contentView.backgroundColor = UIColor.whiteColor()

        let stackView = UIStackView()
        stackView.axis = .Horizontal
        stackView.alignment = .Leading
        stackView.distribution = .EqualSpacing
        stackView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(stackView)

        let leftStack = UIStackView()
        leftStack.axis = .Vertical

        let rightStack = UIStackView()
        rightStack.axis = .Vertical

        stackView.addArrangedSubview(leftStack)
        stackView.addArrangedSubview(rightStack)

        for index in 0...10 {
            let leftLabel = UILabel()
            leftLabel.text = "\(index)"
            leftStack.addArrangedSubview(leftLabel)

            labelArray.append(leftLabel)

            let rightLabel = UILabel()
            rightLabel.text = "\(index)"
            rightStack.addArrangedSubview(rightLabel)

            labelArray.append(rightLabel)
        }


        let constraints = [
            "H:|[stackView]|",
            "V:|[stackView]|"
            ].flatMap {
                NSLayoutConstraint.constraintsWithVisualFormat($0, options: [], metrics: nil, views: ["stackView": stackView])
        }

        NSLayoutConstraint.activateConstraints(constraints)

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

You'll notice the scrolling is smooth when you comment out the call to fillLabels.

If you try to reproduce the same layout without UIStackViews and include the call fillLabels, you'll notice the scrolling is smooth too.

This suggests UIStackView suffers performance bottlenecks if it has recalculate its layout.

Is this hypothesis correct? Are there some solutions?

Eric
  • 16,003
  • 15
  • 87
  • 139

1 Answers1

0

Have you tried not using auto layout? In my experience auto layout is a performance culprit in many situations.

Try to not set translatesAutoresizingMaskIntoConstraints to false (default is true) and also get rid of the constraint based layout of the collection view itself (use autoresizing masks instead).

Werner Altewischer
  • 10,080
  • 4
  • 53
  • 60
  • Upvoted, this is actually true. You can run into exponential performance slowdowns with auto layout. – possen Aug 02 '21 at 03:06