簡體   English   中英

我如何在表格視圖單元格之間繪制這條線。 現在我只設置背景圖像,但單元格大小會增加或減少

[英]How can i draw this line between the tableview cell. For now i just set a background image but cell size will be increase or decrease

在此處輸入圖像描述

如何在表格視圖單元格之間繪制這條線? 現在我只是設置一個背景圖像,但單元格大小會增加或減少。 背景圖像對我來說不是永久的解決方案。

如果您的最大行數小於 50,則使用帶有重復子視圖的普通滾動視圖會比使用表格視圖容易得多。

但是,如果您可能有 100 行或幾百行,您可能會遇到內存問題。

因此,獲得“彎曲虛線”的一種方法是在您的單元格中使用自定義視圖類,該類使用形狀圖層繪制線條。

它看起來像這樣(黃色矩形顯示“單元格”框架):

在此處輸入圖像描述

因此,為形狀層生成貝塞爾路徑的代碼將是:

  • 移動到1
  • 將行添加到2
  • 添加以中心c為中心的圓弧
  • 將行添加到3

形狀線條/筆觸以路徑為中心。 因此,如果我們使用4的線寬,2-points 將在單元格/視圖的頂部上方延伸,2-points 將在底部下方延伸。

如果我們以零垂直間距布置相同的 4 個視圖,並交替右/左/右/左,我們會得到:

在此處輸入圖像描述

然后我們可以在表格視圖單元格中實現它:

在此處輸入圖像描述

在此處輸入圖像描述

在此處輸入圖像描述

雖然會出現幾個問題......

首先,因為行具有可變高度,所以行長會不同。 破折號圖案不會“拉伸以填充”線條,因此末端會有所不同:

在此處輸入圖像描述

如果您的行高於寬度的一半(實際上,小於一半,因為我們允許在兩側留出空間),則會出現另一個問題。

這是什么意思:

在此處輸入圖像描述

當然,這更像是一個設計問題,而不是編碼問題,因為在這種情況下,您需要決定線路的外觀。

這是我用來生成這些圖像的代碼:

枚舉- 用於左/右布局:

enum LayoutDirection: Int {
    case left, right
}

單元格有 3 個標簽- 所以數據的簡單 3 字符串結構:

struct MyDataStruct {
    var first: String = ""
    var second: String = ""
    var third: String = ""
}

PieView - 一個簡單的餅形UIView子類

class PieView: UIView {
    
    private let shapeLayer1 = CAShapeLayer()
    private let shapeLayer2 = CAShapeLayer()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        [shapeLayer1, shapeLayer2].forEach { v in
            layer.addSublayer(v)
            v.fillColor = UIColor.systemOrange.cgColor
            v.strokeColor = UIColor.systemOrange.cgColor
            v.lineWidth = 2
        }
        shapeLayer1.fillColor = UIColor.clear.cgColor
    }
    override func layoutSubviews() {
        super.layoutSubviews()

        var bez: UIBezierPath!
        let ptC: CGPoint = CGPoint(x: bounds.midX, y: bounds.midY)
        let a1: Double = -90.0 * .pi / 180.0
        let a2: Double = 135.0 * .pi / 180.0

        bez = UIBezierPath()
        bez.addArc(withCenter: ptC, radius: bounds.midX, startAngle: a2, endAngle: a1, clockwise: true)
        shapeLayer1.path = bez.cgPath

        bez = UIBezierPath()
        bez.move(to: ptC)
        bez.addArc(withCenter: ptC, radius: bounds.midX, startAngle: a1, endAngle: a2, clockwise: true)
        bez.close()
        shapeLayer2.path = bez.cgPath
    }
    
}

MyDashedArcView - 繪制虛線弧的UIView子類

class MyDashedArcView: UIView {
    
    public var layoutDirection: LayoutDirection = .left {
        didSet {
            setNeedsLayout()
        }
    }
    
    private var shapeLayer: CAShapeLayer!
    
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        shapeLayer = self.layer as? CAShapeLayer
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineWidth = 4
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineDashPattern = [20, 10]
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let inset: CGFloat = 32.0
        let radius: CGFloat = bounds.midY
        var ptC: CGPoint = CGPoint(x: 0.0, y: bounds.midY)
        ptC.x = layoutDirection == .right ? bounds.maxX - (inset + radius) : inset + radius
        let a1: Double = -90.0 * .pi / 180.0
        let a2: Double = 90.0 * .pi / 180.0
        let xOff: CGFloat = 0.0
        
        let bez = UIBezierPath()
        bez.move(to: CGPoint(x: bounds.midX + xOff, y: bounds.minY - 0.0))
        bez.addLine(to: CGPoint(x: ptC.x, y: bounds.minY))
        if layoutDirection == .right {
            bez.addArc(withCenter: ptC, radius: bounds.midY, startAngle: a1, endAngle: a2, clockwise: true)
        } else {
            bez.addArc(withCenter: ptC, radius: bounds.midY, startAngle: a1, endAngle: a2, clockwise: false)
        }
        bez.addLine(to: CGPoint(x: bounds.midX + xOff, y: bounds.maxY))
        shapeLayer.path = bez.cgPath
    }
}

MyPieCell - 表格視圖單元格

class MyPieCell: UITableViewCell {
    
    private var layoutDirection: LayoutDirection = .right {
        didSet {
            
            // update horizontal constraints to position the pieView and labels stack view
            
            let g = contentView
            
            pieHorizontalConstraint.isActive = false
            stackLeadingConstraint.isActive = false
            stackTrailingConstraint.isActive = false
            
            if layoutDirection == .left {
                // pie is on the left
                pieHorizontalConstraint = pieView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 48.0)
                stackLeadingConstraint = stack.leadingAnchor.constraint(equalTo: pieView.trailingAnchor, constant: 20.0)
                stackTrailingConstraint = stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0)
                [firstLabel, secondLabel, thirdLabel].forEach { v in
                    v.textAlignment = .left
                }
            } else {
                // pie is on the right
                pieHorizontalConstraint = pieView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -48.0)
                stackLeadingConstraint = stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0)
                stackTrailingConstraint = stack.trailingAnchor.constraint(equalTo: pieView.leadingAnchor, constant: -20.0)
                [firstLabel, secondLabel, thirdLabel].forEach { v in
                    v.textAlignment = .right
                }
            }
            
            pieHorizontalConstraint.isActive = true
            stackLeadingConstraint.isActive = true
            stackTrailingConstraint.isActive = true
            
        }
    }
    
    func fillData(_ str: MyDataStruct, direction: LayoutDirection) {
        firstLabel.text = str.first
        secondLabel.text = str.second
        thirdLabel.text = str.third
        layoutDirection = direction
        arcView.layoutDirection = direction
    }
    
    private let pieView = PieView()
    private let arcView = MyDashedArcView()
    
    private let firstLabel: UILabel = {
        let v = UILabel()
        v.font = .systemFont(ofSize: 13.0, weight: .regular)
        return v
    }()
    private let secondLabel: UILabel = {
        let v = UILabel()
        v.font = .systemFont(ofSize: 16.0, weight: .bold)
        return v
    }()
    private let thirdLabel: UILabel = {
        let v = UILabel()
        v.font = .systemFont(ofSize: 13.0, weight: .regular)
        v.numberOfLines = 0
        return v
    }()
    
    // stack view for the labels
    private let stack: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.spacing = 2
        return v
    }()
    
    private var pieHorizontalConstraint: NSLayoutConstraint!
    private var stackLeadingConstraint: NSLayoutConstraint!
    private var stackTrailingConstraint: NSLayoutConstraint!
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        
        [firstLabel, secondLabel, thirdLabel].forEach { v in
            v.setContentCompressionResistancePriority(.required, for: .vertical)
            stack.addArrangedSubview(v)
        }
        [arcView, pieView, stack].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview(v)
        }
        
        let g = contentView
        
        // initialize the horizontal constraints that we will update
        //  based on left or right layout
        pieHorizontalConstraint = pieView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0)
        stackLeadingConstraint = stack.leadingAnchor.constraint(equalTo: pieView.trailingAnchor, constant: 20.0)
        stackTrailingConstraint = stack.trailingAnchor.constraint(equalTo: pieView.leadingAnchor, constant: -20.0)
        
        // giving avoid auto-layout complaints
        //  pieView is square (1:1 ratio)
        
        // pieView width constant
        pieView.widthAnchor.constraint(equalToConstant: 60.0).isActive = true
        
        let pieHeightConstraint = pieView.heightAnchor.constraint(equalTo: pieView.widthAnchor)
        pieHeightConstraint.priority = .required - 1
        pieHeightConstraint.isActive = true
        
        NSLayoutConstraint.activate([
            
            // constrain arcView to all 4 sides
            arcView.topAnchor.constraint(equalTo: g.topAnchor),
            arcView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            arcView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            arcView.bottomAnchor.constraint(equalTo: g.bottomAnchor),

            // center the pieView vertically
            pieView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
            
            // we want at least 12-points above and below the pieView
            pieView.topAnchor.constraint(greaterThanOrEqualTo: g.topAnchor, constant: 12.0),
            pieView.bottomAnchor.constraint(lessThanOrEqualTo: g.bottomAnchor, constant: -12.0),
            
            // center the labels stack view vertically
            stack.centerYAnchor.constraint(equalTo: g.centerYAnchor),
            
            // we want at least 12-points above and below the stack view
            stack.topAnchor.constraint(greaterThanOrEqualTo: g.topAnchor, constant: 12.0),
            stack.bottomAnchor.constraint(lessThanOrEqualTo: g.bottomAnchor, constant: -12.0),
            
        ])
        
        // we need to see the table view's background view through the cells
        contentView.backgroundColor = .clear
        self.backgroundColor = .clear
        
        // during development, if we want to see the framing
        //pieView.backgroundColor = .green
        //stack.backgroundColor = .yellow
    }
    
}

SampleTableVC - 帶有表格視圖的示例視圖控制器

class SampleTableVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var myData: [MyDataStruct] = []
    
    let tableView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // generate some sample data
        let sampleStrings: [String] = [
            "Short string.",
            "Medium length string that may or may not wrap.",
            "This is a very long string that will definitely wrap. When running on an iPhone 8 in portrait orientation, it should wrap to four lines.",
        ]
        let thirdLabels: [Int] = [
            0, 1, 0, 1, 0, 1, 2, 0, 1, 2, 1, 2, 2, 2,
        ]
        var rowNum: Int = 0
        thirdLabels.forEach { n in
            var str: MyDataStruct = MyDataStruct()
            str.first = "Level \(rowNum)"
            str.second = "Foundation \(rowNum)"
            str.third = sampleStrings[n % sampleStrings.count]
            myData.append(str)
            rowNum += 1
        }
        // and some more data, with increasing number of lines for the third label
        for i in 4...16 {
            var str: MyDataStruct = MyDataStruct()
            str.first = "Level \(rowNum)"
            str.second = "Foundation \(rowNum)"
            str.third = (1...i).compactMap({"Line \($0)"}).joined(separator: "\n")
            myData.append(str)
            rowNum += 1
        }
        // and a few rows with extremely long strings
        let reallyLongString = "UILabel - A label can contain an arbitrary amount of text, but UILabel may shrink, wrap, or truncate the text, depending on the size of the bounding rectangle and properties you set. You can control the font, text color, alignment, highlighting, and shadowing of the text in the label.\n\nUITextField - Displays a rounded rectangle that can contain editable text. When a user taps a text field, a keyboard appears; when a user taps Return in the keyboard, the keyboard disappears and the text field can handle the input in an application-specific way. UITextField supports overlay views to display additional information, such as a bookmarks icon. UITextField also provides a clear text control a user taps to erase the contents of the text field."
        for _ in 1...5 {
            var str: MyDataStruct = MyDataStruct()
            str.first = "Level \(rowNum)"
            str.second = "Foundation \(rowNum)"
            str.third = reallyLongString
            myData.append(str)
            rowNum += 1
        }
        
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0),
            tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0),
            tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0),
            tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0),
        ])
        
        tableView.register(MyPieCell.self, forCellReuseIdentifier: "c")
        tableView.dataSource = self
        tableView.delegate = self
        tableView.separatorStyle = .none
        
        // because the dashed line will extend above the top of the first cell
        //  and below the bottom of the last cell
        //  we want to add a little "inset padding" on top and bottom of the table view
        
        var defaultInset = tableView.contentInset
        defaultInset.top += 8
        defaultInset.bottom += 8
        tableView.contentInset = defaultInset
        
        tableView.contentInsetAdjustmentBehavior = .never
        tableView.contentOffset.y = -8
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myData.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! MyPieCell
        let dir: LayoutDirection = indexPath.row % 2 == 0 ? .right : .left
        c.fillData(myData[indexPath.row], direction: dir)
        return c
    }
    
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM