简体   繁体   English

在UITableView中滚动时视图被替换

[英]View is getting replaced while Scrolling in UITableView

I am an Android Developer and very new to iOS App Development. 我是一名Android开发人员,对iOS App开发非常陌生。 I am trying to build a simple chat system with only one line of data in each cell. 我正在尝试构建一个简单的聊天系统,每个单元格中只有一行数据。

I am using a custom UIView class to generate a bubble and a UILabel and an UIImageView programmatically. 我正在使用自定义UIView类以编程方式生成气泡, UILabelUIImageView

When I run the app for the first time, everything looks good. 当我第一次运行该应用程序时,一切看起来都不错。 Please see the image below: 请参见下图:

在此处输入图片说明

But, the problem occurs when I scroll the UITableView Upwards and I don't understand why the bubbles shift themselves towards the right side. 但是,当我向上滚动UITableView时,会发生问题,但我不明白为什么气泡会向右移动。 See the image below: 见下图:

在此处输入图片说明

Some post in SO said that it is one of the feature of UITableView . SO中的一些帖子说这是UITableView的功能之一。

How should I solve this? 我该如何解决?

If you need any code snippets from any part of the App, please comment below. 如果您需要该应用程序任何部分的任何代码段,请在下方评论。

PS: I am using Xcode 9 and iOS 11.4 as testing device and Swift 4 programming. PS:我正在使用Xcode 9和iOS 11.4作为测试设备和Swift 4编程。

Any help would be appreciated. 任何帮助,将不胜感激。 Thanks. 谢谢。

Below is BubbleView Class: 下面是BubbleView类:

EDIT 编辑

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()
    }

}

And, The tableView thingy: 而且,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
}

Code Updated as Requested. 代码已按要求更新。

You need to move your code that adds the subviews to the cell from the cellForRowAt delegate to inside their respective cell classes. 您需要将将子视图添加到单元格的代码从cellForRowAt委托移动到其各自的单元格类内部。

The problem right now like @PeteMorris said, is that your cells which will be reused will already have the subviews which you added when it was dequeued the first time. 现在的问题就像@PeteMorris所说的那样,将被重用的单元格已经具有您在第一次出队时添加的子视图。 So whenever the same cell is dequeued, subviews are added to it again . 因此,每当同一单元出队时,子视图就会再次添加到其中。

  • The elegant solution would be to move the code that sets up the view to inside your cell class. 优雅的解决方案是将设置视图的代码移到单元格类内部。

  • If you are looking for a hotfix you could remove all the subviews from the cell in prepareForReuse . 如果要查找修补程序 ,则可以从prepareForReuse的单元格中删除所有子视图。 (This method is called every time your cell is reused before dequeue-ing) (每次在出队之前重用您的单元格时都会调用此方法)

Your reuse method would look like this. 您的重用方法将如下所示。 (This method is inside your cell class) (此方法在您的单元格类中)

override func prepareForReuse() {
    super.prepareForReuse()

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM