![](/img/trans.png)
[英]I have collectionView inside of Tableview cell. It return always Zero. How can I solve it?
[英]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.