草庐IT

ios - 带有 UITableViewCells + AutoLayout 的 UITableView - 不像*应该*那样平滑

coder 2023-09-09 原文

我最近发布了一个关于带有自定义 UITableCells 的 UITableView 的问题,当使用 AutoLayout 定位单元格的 subview 时,该问题并不流畅。我收到一些评论,表明缺乏平滑度是由于单元格的复杂布局造成的。虽然我同意单元格布局越复杂,tableView 必须进行更多计算才能获得单元格的高度,但我认为 10-12 UIView 和 UILabel subview 不会导致我在滚动时看到的延迟量一台 iPad。

因此,为了进一步证明我的观点,我创建了一个 UIViewController 项目,其中包含一个 UITableView subview 和自定义 UITableViewCells,其子类中只有 2 个标签。而且滚动仍然不是很流畅。从我的角度来看,这是您可以获得的最基本的 - 因此,如果 UITableView 仍然无法满足此设计的要求,那么我一定是遗漏了一些东西。

下面使用的 110 的 estimatedRowHeight 是对实际行高平均值的非常接近的估计。当我使用“用户界面检查器”并逐个查看每个单元格的高度时,它们的范围是 103 - 124。

请记住,当我将下面的代码切换为时,使用estimatedRowHeightUITableViewAutomaticDimension 并改为实现func tableView( tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {,用帧值计算高度,UITableView 像黄油一样滚动。

App截图(供引用)

应用源代码(滚动不是很流畅)

// The custom `Quote` object that holds the
// properties for our data mdoel
class Quote {
    var text: String!
    var author: String!

    init(text: String, author: String) {
        self.text = text
        self.author = author
    }
}


// Custom UITableView Cell, using AutoLayout to
// position both a "labelText" (the quote itself)
// and "labelAuthor" (the author's name) label
class CellQuote: UITableViewCell {
    private var containerView: UIView!
    private var labelText: UILabel!
    private var labelAuthor: UILabel!


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

        contentView.backgroundColor = UIColor.whiteColor()

        containerView = UIView()
        containerView.backgroundColor = UIColor(
            red: 237/255,
            green: 237/255,
            blue: 237/255,
            alpha: 1.0
        )
        contentView.addSubview(containerView)
        containerView.translatesAutoresizingMaskIntoConstraints = false
        containerView.leadingAnchor.constraintEqualToAnchor(contentView.leadingAnchor, constant: 0).active = true
        containerView.trailingAnchor.constraintEqualToAnchor(contentView.trailingAnchor, constant: 0).active = true
        containerView.topAnchor.constraintEqualToAnchor(contentView.topAnchor, constant: 4).active = true
        containerView.bottomAnchor.constraintEqualToAnchor(contentView.bottomAnchor, constant: 0).active = true

        labelText = UILabel()
        labelText.numberOfLines = 0
        labelText.font = UIFont.systemFontOfSize(18)
        labelText.textColor = UIColor.darkGrayColor()

        containerView.addSubview(labelText)
        labelText.translatesAutoresizingMaskIntoConstraints = false
        labelText.leadingAnchor.constraintEqualToAnchor(containerView.leadingAnchor, constant: 20).active = true
        labelText.topAnchor.constraintEqualToAnchor(containerView.topAnchor, constant: 20).active = true
        labelText.trailingAnchor.constraintEqualToAnchor(containerView.trailingAnchor, constant: -20).active = true

        labelAuthor = UILabel()
        labelAuthor.numberOfLines = 0
        labelAuthor.font = UIFont.boldSystemFontOfSize(18)
        labelAuthor.textColor = UIColor.blackColor()

        containerView.addSubview(labelAuthor)
        labelAuthor.translatesAutoresizingMaskIntoConstraints = false
        labelAuthor.topAnchor.constraintEqualToAnchor(labelText.bottomAnchor, constant: 20).active = true
        labelAuthor.leadingAnchor.constraintEqualToAnchor(containerView.leadingAnchor, constant: 20).active = true
        labelAuthor.trailingAnchor.constraintEqualToAnchor(containerView.trailingAnchor, constant: -20).active = true
        labelAuthor.bottomAnchor.constraintEqualToAnchor(containerView.bottomAnchor, constant: -20).active = true

        self.selectionStyle = UITableViewCellSelectionStyle.None
    }

    func configureWithData(quote: Quote) {
        labelText.text = quote.text
        labelAuthor.text = quote.author
    }

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

}

// The UIViewController that is a 
class ViewController: UIViewController, UITableViewDataSource {

    var tableView: UITableView!
    var dataItems: [Quote]!

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView = UITableView()
        tableView.dataSource = self

        tableView.registerClass(CellQuote.self, forCellReuseIdentifier: "cellQuoteId")

        tableView.backgroundColor = UIColor.whiteColor()
        tableView.separatorStyle = UITableViewCellSeparatorStyle.None
        tableView.estimatedRowHeight = 110
        tableView.rowHeight = UITableViewAutomaticDimension

        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
        tableView.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 20).active = true
        tableView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true
        tableView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor).active = true

        dataItems = [
            Quote(text: "One kernel is felt in a hogshead; one drop of water helps to swell the ocean; a spark of fire helps to give light to the world. None are too small, too feeble, too poor to be of service. Think of this and act.", author: "Michael.Frederick"),
            Quote(text: "A timid person is frightened before a danger, a coward during the time, and a courageous person afterward.", author: "Lorem.Approbantibus."),
            Quote(text: "There is only one way to defeat the enemy, and that is to write as well as one can. The best argument is an undeniably good book.", author: "Lorem.Fruitur."),
            // ... many more quotes ...
        ]

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: - UITableViewDataSource

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataItems.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cellQuoteId") as! CellQuote
        cell.configureWithData(dataItems[indexPath.row])
        return cell
    }
}

我喜欢下面 matt 的建议,但我仍在尝试调整它以使其适合我:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    var cellHeights: [CGFloat] = [CGFloat]()

    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        if cellHeights.count == 0 {
            var cellHeights = [CGFloat]()
            let numQuotes: Int = dataItems.count

            for index in 0...numQuotes - 1 {
                let cell = CellQuote()
                let quote = dataItems[index]

                cell.configureWithData(quote)
                let size = cell.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
                cellHeights.append(size.height)
            }

            self.cellHeights = cellHeights
        }

        return self.cellHeights[indexPath.row]

    }
}

最佳答案

我从未发现自动行高机制与我们在自动布局出现之前使用的旧计算布局技术一样流畅。使用 Instruments 很容易看出瓶颈,即运行时必须在每个新单元格滚动到 View 中时调用 systemLayoutSizeFittingSize:

在我的书中,我演示了我的首选技术,即在表格 View 首次出现时计算所有单元格的高度一次。这意味着我可以从那时起立即为 heightForRowAtIndexPath 提供答案,从而获得最佳的用户体验。此外,如果您随后将对 dequeueReusableCellWithIdentifier 的调用替换为更好、更现代的 dequeueReusableCellWithIdentifier:forIndexPath,那么您的优势在于,单元格 正确的大小,并且在那之后不需要进一步的布局。

关于ios - 带有 UITableViewCells + AutoLayout 的 UITableView - 不像*应该*那样平滑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35977792/

有关ios - 带有 UITableViewCells + AutoLayout 的 UITableView - 不像*应该*那样平滑的更多相关文章

  1. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  4. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  5. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  6. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  7. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  8. ruby-on-rails - 带有 Zeus 的 RSpec 3.1,我应该在 spec_helper 中要求 'rspec/rails' 吗? - 2

    使用rspec-rails3.0+,测试设置分为spec_helper和rails_helper我注意到生成的spec_helper不需要'rspec/rails'。这会导致zeus崩溃:spec_helper.rb:5:in`':undefinedmethod`configure'forRSpec:Module(NoMethodError)对thisissue最常见的回应是需要'rspec/rails'。但这是否会破坏仅使用spec_helper拆分rails规范和PORO规范的全部目的?或者这无关紧要,因为Zeus无论如何都会预加载Rails?我应该在我的spec_helper中做

  9. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  10. Ruby:如何使用带有散列的 'send' 方法调用方法? - 2

    假设我有一个类A,里面有一些方法。假设stringmethodName是这些方法之一,我已经知道我想给它什么参数。它们在散列中{'param1'=>value1,'param2'=>value2}所以我有:params={'param1'=>value1,'param2'=>value2}a=A.new()a.send(methodName,value1,value2)#callmethodnamewithbothparams我希望能够通过传递我的哈希以某种方式调用该方法。这可能吗? 最佳答案 确保methodName是一个符号,而

随机推荐