iOS 9之后UITableView中的滚动性能不好(以前工作得很好)

iOS 9之后UITableView中的滚动性能不好(以前工作得很好),ios,swift,uitableview,autolayout,uitextview,Ios,Swift,Uitableview,Autolayout,Uitextview,自从升级到iOS9之后,以前运行良好的代码在滚动时开始显示出非常糟糕的性能 我试图从头开始创建一个最小的示例,省去了大部分单元格的配置和各种缓存和内容生成代码。然而,代码仍然不是一个快速阅读的工具,而且分析结果也有所不同:/ 根据苹果开发者论坛(Edit:我发现)这可能是由于自动布局的改变,以及在superView的后面有大量的布局视图使得它不那么糟糕,仍然不足以发布 我怀疑问题出在UIKit中,尤其是在自动布局和UITextView中,因为当我开始设置cell.textView.text时,滚

自从升级到iOS9之后,以前运行良好的代码在滚动时开始显示出非常糟糕的性能

我试图从头开始创建一个最小的示例,省去了大部分单元格的配置和各种缓存和内容生成代码。然而,代码仍然不是一个快速阅读的工具,而且分析结果也有所不同:/

根据苹果开发者论坛(Edit:我发现)这可能是由于自动布局的改变,以及在superView的后面有大量的布局视图使得它不那么糟糕,仍然不足以发布

我怀疑问题出在
UIKit
中,尤其是在自动布局和
UITextView
中,因为当我开始设置
cell.textView.text
时,滚动立即变得笨重

有没有人做过类似的观察?对许多人来说,这似乎不是什么问题

有什么建议可以让这更顺利吗?使用
UILabel
s可以修复它,但是在现实生活中,属性字符串中有一些链接是可以被点击的,所以我认为没有办法避免
UITextView

我使用的是自定义的
UITableViewCell
子类,我也简化了这些子类,但由于编程的自动布局,它们仍然很受欢迎:

class AbstractRegularTimelineCell: UITableViewCell {
    weak var iconView : UIImageView!
    weak var headerLabel : UILabel!
    weak var timeLabel : UILabel!
    weak var regularContentContainer : UIView!
    weak var contentHeightConstraint : NSLayoutConstraint!
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

    private func commonInit() {
        self.selectionStyle = .None
        // add the UI elements needed for all regular cells.

        let iv = UIImageView(/*image: UIImage(named: "icon")*/)
        iv.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(iv)
        self.iconView = iv
        iv.setContentCompressionResistancePriority(1000, forAxis: .Horizontal)
        iv.setContentCompressionResistancePriority(1000, forAxis: .Vertical)


        let hl = UILabel()
        hl.numberOfLines = 0
        hl.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(hl)
        self.headerLabel = hl
        hl.font = UIFont(name: "OpenSans-Semibold", size: UIFont.systemFontSize())

        let tiv = UIImageView(/*image: UIImage(named: "timestamp")*/)
        tiv.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(tiv)


        let tl = UILabel()
        tl.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(tl)
        self.timeLabel = tl

        tl.textColor = UIColor.lightGrayColor()
        tl.font = UIFont(name: "OpenSans", size: UIFont.systemFontSize()-3)

        let rcc = UIView()
        rcc.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(rcc)
        self.regularContentContainer = rcc
        rcc.setContentCompressionResistancePriority(1000, forAxis: .Vertical)
        rcc.setContentCompressionResistancePriority(100, forAxis: .Horizontal)
        self.contentView.sendSubviewToBack(rcc) // this might help, according to Apple Dev Forums




        // now, stitch the constraints together.
        let views = ["iv":iv, "hl":hl, "tl":tl, "rcc":rcc, "tiv":tiv]



        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(15)-[iv]-(15)-[hl]-(>=15)-|", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: [:], views: views))

        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(15)-[iv]", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: nil, views: views))

        self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(17)-[hl]-(8)-[rcc]", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: nil, views: views))


        var otherConstraints = [NSLayoutConstraint]()
        var c = NSLayoutConstraint(item: tiv, attribute: .Top, relatedBy: .Equal, toItem: rcc, attribute: .Bottom, multiplier: 1, constant: 8)

        c.priority = 600
        otherConstraints.append(c)

        c = NSLayoutConstraint(item: tiv, attribute: .Top, relatedBy: .GreaterThanOrEqual, toItem: rcc, attribute: .Bottom, multiplier: 1, constant: 6)

        otherConstraints.append(c)

        c = NSLayoutConstraint(item: tl, attribute: .Leading, relatedBy: .Equal, toItem: tiv, attribute: .Trailing, multiplier: 1, constant: 6)

        otherConstraints.append(c)

        c = NSLayoutConstraint(item: tiv, attribute: .CenterY, relatedBy: .Equal, toItem: tl, attribute: .CenterY, multiplier: 1, constant: 1)

        otherConstraints.append(c)

        c = NSLayoutConstraint(item: self.contentView, attribute: .Bottom, relatedBy: .Equal, toItem: tiv, attribute: .Bottom, multiplier: 1, constant: 10)

        otherConstraints.append(c)

        c = NSLayoutConstraint(item: tiv, attribute: .Leading, relatedBy: .Equal, toItem: hl, attribute: .Leading, multiplier: 1, constant: 0)

        otherConstraints.append(c)


        c = NSLayoutConstraint(item: rcc, attribute: NSLayoutAttribute.Height, relatedBy: .LessThanOrEqual, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 10000)

        otherConstraints.append(c)
        self.contentHeightConstraint = c

        c = NSLayoutConstraint(item: self.contentView, attribute: .Trailing, relatedBy: .GreaterThanOrEqual, toItem: rcc, attribute: .Trailing, multiplier: 1, constant: 15)
        otherConstraints.append(c)

        c = NSLayoutConstraint(item: rcc, attribute: .Leading, relatedBy: .Equal, toItem: hl, attribute: .Leading, multiplier: 1, constant: 0)
        otherConstraints.append(c)

        self.contentView.addConstraints(otherConstraints)
    }

}



class ExampleCell: AbstractRegularTimelineCell {
    weak var textView : UITextView!
    override required init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.setupTextView()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupTextView()
    }
    private func setupTextView() {
        let t = UITextView()
        t.translatesAutoresizingMaskIntoConstraints = false
        self.regularContentContainer.addSubview(t)
        self.regularContentContainer.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[t]|", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: [:], views: ["t":t]))
        self.regularContentContainer.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[t]|", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: [:], views: ["t":t]))
        self.textView = t
        t.scrollEnabled = false
    }
}
不太长的视图控制器代码:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    weak var tableView : UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()
        let t = UITableView()
        t.registerClass(ExampleCell.classForCoder(), forCellReuseIdentifier: "example")
        t.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(t)
        self.tableView = t
        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[t]|", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: [:], views: ["t":t]))
        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[t]|", options: NSLayoutFormatOptions.DirectionLeadingToTrailing, metrics: [:], views: ["t":t]))
        t.delegate = self
        t.dataSource = self
        t.estimatedRowHeight = 350
    }


    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 300
    }
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell : ExampleCell = tableView.dequeueReusableCellWithIdentifier("example", forIndexPath: indexPath) as! ExampleCell
        cell.timeLabel.text = "probably never"
        switch indexPath.row % 3 {
        case 0:
            cell.headerLabel.text = "First paragraph (Part one of two)"
            cell.textView.text = "As any dedicated reader can clearly see, the Ideal of practical reason is a representation of, as far as I know, the things in themselves; as I have shown elsewhere, the phenomena should only be used as a canon for our understanding. These paralogisms of practical reason are what first give rise to the architectonic of practical reason."
        case 1:
            cell.headerLabel.text = "First paragraph, 2/2"
            cell.textView.text = "As will easily be shown in the next section, reason would thereby be made to contradict, in view of these considerations, the Ideal of practical reason, yet the manifold depends on the phenomena. Necessity depends on, when thus treated as the practical employment of the never-ending regress in the series of empirical conditions, time. Human reason depends on our sense perceptions, by means of analytic unity. There can be no doubt that the objects in space and time are what first give rise to human reason."
        default:
            cell.headerLabel.text = "Second paragraph"
            cell.textView.text = "Let us suppose that the noumena have nothing to do with necessity, since knowledge of the Categories is a posteriori. Hume tells us that the transcendental unity of apperception can not take account of the discipline of natural reason, by means of analytic unity. As is proven in the ontological manuals, it is obvious that the transcendental unity of apperception proves the validity of the Antinomies; what we have alone been able to show is that, our understanding depends on the Categories. It remains a mystery why the Ideal stands in need of reason. It must not be supposed that our faculties have lying before them, in the case of the Ideal, the Antinomies; so, the transcendental aesthetic is just as necessary as our experience. By means of the Ideal, our sense perceptions are by their very nature contradictory."
        }
    return cell
    }
}

正如@A-Live在评论中指出的,问题在于嵌套的滚动视图

为了使这个问题对任何人都有用,我最终用s替换了
UITextView
s,这大大提高了性能


此外,还无法解释为什么iOS 9更新后性能会显著下降;我怀疑对autolayout引擎进行了未记录的更改;有了一个很好的解决方案,就不需要进一步研究了。

如果您嵌套了滚动视图,则这些视图的性能可能会很差。如果您只需要可单击的链接,那么用标签替换文本视图并不难,举个简单的例子。