Bad performance of UIStackView within UICollectionViewCells

Originator:ariel.elkin
Number:rdar://24206043 Date Originated:2016/01/15
Status:Open Resolved:No
Product:iOS SDK Product Version:9.2
Classification:Performance issue Reproducible:Yes
 
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.

Comments

any update on this?

By hernan.canzonetta at March 4, 2017, 10:19 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!