在UITableView中滚动时视图被替换 [英] View is getting replaced while Scrolling in UITableView

查看:57
本文介绍了在UITableView中滚动时视图被替换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一名Android开发人员,并且是iOS App开发的新手.我正在尝试构建一个简单的聊天系统,每个单元格中只有一行数据.

I am an Android Developer and very new to iOS App Development. I am trying to build a simple chat system with only one line of data in each cell.

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

I am using a custom UIView class to generate a bubble and a UILabel and an UIImageView programmatically.

当我第一次运行该应用程序时,一切看起来都不错.请参见下图:

When I run the app for the first time, everything looks good. Please see the image below:

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

But, the problem occurs when I scroll the UITableView Upwards and I don't understand why the bubbles shift themselves towards the right side. See the image below:

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

Some post in SO said that it is one of the feature of UITableView.

我该如何解决?

如果您需要该应用程序任何部分的任何代码段,请在下面发表评论.

If you need any code snippets from any part of the App, please comment below.

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

PS: I am using Xcode 9 and iOS 11.4 as testing device and Swift 4 programming.

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

Any help would be appreciated. Thanks.

以下是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很重要:

And, The tableView thingy:

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
}

代码已按要求更新.

推荐答案

您需要将将子视图添加到单元格的代码从cellForRowAt委托移至其各自的单元格类内部.

You need to move your code that adds the subviews to the cell from the cellForRowAt delegate to inside their respective cell classes.

现在的问题就像@PeteMorris所说的那样,您的将要重用的单元已经已经具有您在第一次出队时添加的子视图.因此,每当同一个单元出队时,就会再次 向其添加子视图.

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. 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.

如果要查找修补程序,则可以从prepareForReuse中的单元格中删除所有子视图. (每次在出队之前重用您的单元格时都会调用此方法)

If you are looking for a hotfix you could remove all the subviews from the cell in 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()
    }
}

这篇关于在UITableView中滚动时视图被替换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆