【问题标题】:View is getting replaced while Scrolling in UITableView在 UITableView 中滚动时视图被替换
【发布时间】:2018-09-10 13:32:56
【问题描述】:

我是一名 Android 开发人员,对 iOS 应用开发非常陌生。我正在尝试构建一个简单的聊天系统,每个单元格中只有一行数据。

我正在使用自定义 UIView 类以编程方式生成气泡、UILabelUIImageView

当我第一次运行该应用程序时,一切看起来都很好。请看下图:

但是,当我向上滚动UITableView 时会出现问题,我不明白为什么气泡会向右侧移动。见下图:

SO中的一些帖子说这是UITableView的功能之一。

我应该如何解决这个问题?

如果您需要应用程序任何部分的任何代码sn-ps,请在下方评论。

PS:我使用 Xcode 9 和 iOS 11.4 作为测试设备和 Swift 4 编程。

任何帮助将不胜感激。谢谢。

下面是BubbleView类:

编辑

import UIKit
import Foundation

class BubbleView: UIView {

    var incomingColor = UIColor(white: 0.9, alpha: 1)
    var outgoingColor = UIColor(red: 0.09, green: 0.54, blue: 1, alpha: 1)

    var isIncoming: Bool = false

    init(isIncoming: Bool) {
        self.isIncoming = isIncoming
        super.init(frame: UIScreen.main.bounds);
    }

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)
    }

    override func draw(_ rect: CGRect) {
        let width = rect.width
        let height = rect.height

        let bezierPath = UIBezierPath()

        if self.isIncoming == true {
            bezierPath.move(to: CGPoint(x: 22, y: height))
            bezierPath.addLine(to: CGPoint(x: width - 17, y: height))
            bezierPath.addCurve(to: CGPoint(x: width, y: height - 17), controlPoint1: CGPoint(x: width - 7.61, y: height), controlPoint2: CGPoint(x: width, y: height - 7.61))
            bezierPath.addLine(to: CGPoint(x: width, y: 17))
            bezierPath.addCurve(to: CGPoint(x: width - 17, y: 0), controlPoint1: CGPoint(x: width, y: 7.61), controlPoint2: CGPoint(x: width - 7.61, y: 0))
            bezierPath.addLine(to: CGPoint(x: 21, y: 0))
            bezierPath.addCurve(to: CGPoint(x: 4, y: 17), controlPoint1: CGPoint(x: 11.61, y: 0), controlPoint2: CGPoint(x: 4, y: 7.61))
            bezierPath.addLine(to: CGPoint(x: 4, y: height - 11))
            bezierPath.addCurve(to: CGPoint(x: 0, y: height), controlPoint1: CGPoint(x: 4, y: height - 1), controlPoint2: CGPoint(x: 0, y: height))
            bezierPath.addLine(to: CGPoint(x: -0.05, y: height - 0.01))
            bezierPath.addCurve(to: CGPoint(x: 11.04, y: height - 4.04), controlPoint1: CGPoint(x: 4.07, y: height + 0.43), controlPoint2: CGPoint(x: 8.16, y: height - 1.06))
            bezierPath.addCurve(to: CGPoint(x: 22, y: height), controlPoint1: CGPoint(x: 16, y: height), controlPoint2: CGPoint(x: 19, y: height))

            incomingColor.setFill()

        } else {
            bezierPath.move(to: CGPoint(x: width - 22, y: height))
            bezierPath.addLine(to: CGPoint(x: 17, y: height))
            bezierPath.addCurve(to: CGPoint(x: 0, y: height - 17), controlPoint1: CGPoint(x: 7.61, y: height), controlPoint2: CGPoint(x: 0, y: height - 7.61))
            bezierPath.addLine(to: CGPoint(x: 0, y: 17))
            bezierPath.addCurve(to: CGPoint(x: 17, y: 0), controlPoint1: CGPoint(x: 0, y: 7.61), controlPoint2: CGPoint(x: 7.61, y: 0))
            bezierPath.addLine(to: CGPoint(x: width - 21, y: 0))
            bezierPath.addCurve(to: CGPoint(x: width - 4, y: 17), controlPoint1: CGPoint(x: width - 11.61, y: 0), controlPoint2: CGPoint(x: width - 4, y: 7.61))
            bezierPath.addLine(to: CGPoint(x: width - 4, y: height - 11))
            bezierPath.addCurve(to: CGPoint(x: width, y: height), controlPoint1: CGPoint(x: width - 4, y: height - 1), controlPoint2: CGPoint(x: width, y: height))
            bezierPath.addLine(to: CGPoint(x: width + 0.05, y: height - 0.01))
            bezierPath.addCurve(to: CGPoint(x: width - 11.04, y: height - 4.04), controlPoint1: CGPoint(x: width - 4.07, y: height + 0.43), controlPoint2: CGPoint(x: width - 8.16, y: height - 1.06))
            bezierPath.addCurve(to: CGPoint(x: width - 22, y: height), controlPoint1: CGPoint(x: width - 16, y: height), controlPoint2: CGPoint(x: width - 19, y: height))

            outgoingColor.setFill()
        }

        bezierPath.close()
        bezierPath.fill()
    }

}

还有,tableView 的东西:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "BiddingHistoryIdentifier", for: indexPath) as! BiddingHistoryTableViewCell

    let row = indexPath.row

    var strNew: [String] = splitString[row].components(separatedBy: "-")
    let strFirst = strNew[0].trimmingCharacters(in: .whitespacesAndNewlines)
    let strSecond = strNew[1].trimmingCharacters(in: .whitespacesAndNewlines)

    if !strFirst.elementsEqual(VendorLoginSetterGetter.strName.trimmingCharacters(in: .whitespacesAndNewlines)) {

        let text = strSecond
        let label =  UILabel()
        label.numberOfLines = 0
        label.font = UIFont.systemFont(ofSize: 18)
        label.textColor = .black
        label.text = text

        let constraintRect = CGSize(width: 0.66 * cell.frame.width,
                                    height: .greatestFiniteMagnitude)
        let boundingBox = text.boundingRect(with: constraintRect,
                                            options: .usesLineFragmentOrigin,
                                            attributes: [.font: label.font],
                                            context: nil)
        label.frame.size = CGSize(width: ceil(boundingBox.width),
                                    height: ceil(boundingBox.height))

        let bubbleSize = CGSize(width: label.frame.width + 28,
                                height: label.frame.height + 20)

        let bubbleView = BubbleView(isIncoming: true)
        bubbleView.frame.size = bubbleSize
        bubbleView.backgroundColor = .clear

        //bubbleView.center = cell.center
        bubbleView.frame.origin.y = (cell.frame.size.height / 2) - 20
        bubbleView.frame.origin.x = 50
        cell.addSubview(bubbleView)

        label.center = bubbleView.center
        cell.addSubview(label)

        let imageView = UIImageView(frame: CGRect(x: 8, y: 0, width: 35, height: 35))
        imageView.image = UIImage(named: "ic_vendor_black.png")
        imageView.frame.origin.y = (cell.frame.size.height / 2) - 15
        cell.addSubview(imageView)

    }
    else {

        let text = strSecond
        let label =  UILabel()
        label.numberOfLines = 0
        label.font = UIFont.systemFont(ofSize: 18)
        label.textColor = .white
        label.text = text

        let constraintRect = CGSize(width: 0.66 * cell.frame.width,
                                    height: .greatestFiniteMagnitude)
        let boundingBox = text.boundingRect(with: constraintRect,
                                            options: .usesLineFragmentOrigin,
                                            attributes: [.font: label.font],
                                            context: nil)
        label.frame.size = CGSize(width: ceil(boundingBox.width),
                                  height: ceil(boundingBox.height))

        let bubbleSize = CGSize(width: label.frame.width + 28,
                                height: label.frame.height + 20)

        let bubbleView = BubbleView(isIncoming: false)
        bubbleView.frame.size = bubbleSize
        bubbleView.backgroundColor = .clear

        //bubbleView.center = cell.center
        bubbleView.frame.origin.y = (cell.frame.size.height / 2) - 20
        bubbleView.frame.origin.x = cell.frame.size.width - 200
        cell.addSubview(bubbleView)

        label.center = bubbleView.center
        cell.addSubview(label)

        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 28, height: 28))
        imageView.image = UIImage(named: "ic_user_black.png")
        imageView.frame.origin.y = (cell.frame.size.height / 2) - 15
        imageView.frame.origin.x = cell.frame.size.width - 8
        cell.addSubview(imageView)

    }
    return cell
}

已按要求更新代码。

【问题讨论】:

  • 这可能是您的行重用策略的问题,尽管它看起来也可能与重复数据有关。当您有 5 个不同的号码而不是三个 580 号码时,是否会出现同样的问题?您需要提供有关如何创建和填充每个单元格的更多信息。
  • 在我看来,当一个单元格被重用时,您可能正在添加另一个 UILabel 和 UIImageView,而不是更改已经存在的文本。看看您是如何在示例中的两个单元格中的旧标签之上添加第二个标签的?如果您可以发布您的 UITableViewDataSource 实现,我相信有人可以提供帮助。
  • 你能用tableview展示你的班级吗?
  • @IanMacDonald 是的,当数字也不同时,它确实会出现。我将整个 Bubble Class 代码粘贴到我的帖子中。
  • @PeteMorris 你的回答很有道理。每次从服务器传入新数据时,我都会添加一个视图。我会发布代码。

标签: ios swift uitableview


【解决方案1】:

您需要将用于将子视图添加到单元格的代码从 cellForRowAt 委托移动到它们各自的单元格类中。

现在的问题就像@PeteMorris 所说的那样,是您将被重用的单元格将已经具有您在第一次出队时添加的子视图。因此,每当同一单元格出列时,子视图都会再次添加到其中。

  • 优雅的解决方案是将设置视图的代码移到单元格类中。

  • 如果您正在寻找修补程序,您可以从prepareForReuse 的单元格中删除所有子视图。 (每次你的单元在出队之前被重用时都会调用这个方法)

您的重用方法如下所示。 (此方法在您的单元类中)

override func prepareForReuse() {
    super.prepareForReuse()

    for view in subviews {
        view.removeFromSuperview()
    }
}

【讨论】:

  • 哦,好的,会不会出现向上滚动时右移的问题也是因为这个。。有了这个方案,也解决了。?
  • @UmangBurman 我没有检查你的框架数学。但如果它第一次正确设置,将代码移动到单元格应该可以修复它。不过,我强烈建议您探索自动布局来定位视图,因为它们比框架更灵活。
  • 感谢您的回答和解释 :) 我会试试这个,嗯,我现在不在,所以现在不能尝试.. 但是,我会支持支持.再一次感谢你。 :)
  • 我确实修复了观看次数翻倍的问题,但移动问题仍然存在.. :(
  • @UmangBurman 这应该不难。只需在设置框架的位置保留一个断点并检查它的值。
猜你喜欢
  • 2014-10-12
  • 1970-01-01
  • 2021-03-24
  • 2019-11-07
  • 1970-01-01
  • 2012-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多