Search code examples
iosswiftipaduitextviewinstruments

Initial lag for a cell with a UITextView


Current Setup

I have a custom cell, loaded from a xib, where most of its space is covered by UITextview. Also that cell may have a few textviews. And there are few more elements (one UIView + 2 UILabels) inside of this cell.

The Problem

I tried removing all those views and lag stays even if I have only one textview. Also, the lag is happening only for the first time. Later on, when I scroll down, and run into another cell with a textview in it, the lag doesn't happen at all.

Additional Info

The thing with this custom cell is that a textview is added to a UIStackView. At the beginning, a stackview is empty, because I don't know (at development time) how many textviews may/should be there.

I am aware that this is another thing that might affect on performance, but I have solved it ( I guess as best as it could) by checking how many textviews are already found in stackview's arrangedSubviews array when dequeuing a cell, and based on that info, I just add or hide views appropriately (rather than to destroying, and re-creating a required number of textviews each time).

I have tried using Instruments, but I didn't noticed that any of my classes take up CPU time, but rather some UIKit method calls that are called by the framework internally are the cause of this... If needed, I can post a screenshot, but I guess this is not relevant because those seem to be the usual system & framework calls. Plus I am testing on iPad 2 :D so maybe that is a thing (I have to optimize an app for slow devices).

Still, I guess I can optimize this somehow?

The MyCell class is rather simple (pseudo code):

class MyCell:UITableViewCell{

    func configure(data:SomeData){

        self.addOrHideViewsIfNeeded()
    }

    private func addOrHideViewsIfNeeded(){
        //here, I check if stackview.arrangedSubviews has, and how many subviews are there, and
        //add / hide them appropriately, means if I have to add them, I load them from the nib, otherwise, I reuse views from by adding them/removing them from a pool.
    }
}

Also the lag is more noticealbe in Debug version in compare to Release version, which make sense, but it is still noticeable.


Solution

  • Ok this is a sketch of my idea to do the preloading in an invisible from user perspective tableview row zero.

    class MyCell : UITableViewCell {
        static var initiallyPreloaded : Bool = false
    
        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
    
            if !MyCell.initiallyPreloaded {
    //Do the initial preloading setup by adding UITextView to self.contentView
                MyCell.initiallyPreloaded = true
            } else {
    //Setup the regular cell content otherwise
            }
        }
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    }
    
    class ViewController: UIViewController , UITableViewDelegate {
        func tableView(_ tableView: UITableView,
                       cellForRowAt indexPath: IndexPath) -> UITableViewCell{
            var cell: MyCell? = tableView.dequeueReusableCell(withIdentifier: "myCellIdentifier") as! MyCell?
            if cell == nil {
                cell = MyCell(style: .default, reuseIdentifier: "myCellIdentifier")
            }
            if indexPath.row == 0 {
    //Display the initial cell in unnoticeable to user way (start with hidden/alpha zero)
    //For this zero-th row the initial preloading should happen
            }
            return cell!
        }
    }