UITableCellView自动调整大小以填充整个屏幕

时间:2020-07-23 20:58:29

标签: swift uitableview uistackview

我使用以下initLayout函数初始化了UITableViewCell:

        contentView.addSubview(horizontalStack)
        horizontalStack.addArrangedSubview(imageToView)
        imageToView.contentMode = .scaleAspectFit
        verticalStack.addArrangedSubview(petName)
        verticalStack.addArrangedSubview(petDescription)
        horizontalStack.addArrangedSubview(verticalStack)

但这给了我以下结果。图片太大了,线条都弄乱了,标题的上方是顶部:

image

我希望视图看起来像这样。注意所有元素的对称性

image

到目前为止,我已经尝试过:

  1. UITableViewCell是具有2个元素的水平堆栈视图,即:
  • 图像视图
  • 具有以下内容的垂直堆栈视图:
  • 两个标签

有人可以帮我吗?

1 个答案:

答案 0 :(得分:1)

堆栈视图在安排子视图方面做得很好-但是您必须给它们提供足够的信息,以便他们知道 如何 您希望它们进行安排。

首先,我假设您添加了horizontalStack的约束条件(否则,您的屏幕截图将看不到任何内容)。

在不提供任何其他约束的情况下,堆栈视图使用子视图的intrinsicContentSize处理该安排。如果您没有给UIImageView任何约束,则其固有大小将是图像的大小。

假设您要使图像为正方形(比例为1:1),请为其设置heightAnchor = widthAnchor约束。

imageToView.heightAnchor.constraint(equalTo: imageToView.widthAnchor)

然后,确定您想要的宽度。定点宽度-例如80?

imageToView.widthAnchor.constraint(equalToConstant: 80.0)

还是相对宽度(例如标签宽度的1/2)?

imageToView.widthAnchor.constraint(equalTo: verticalStack.widthAnchor, multiplier: 0.5)

您还需要决定如何在horizontalStack中对齐。

这里有几个例子。第一部分使用horizontalStack.alignment = .center,第二部分使用horizontalStack.alignment = .top(右侧的图像具有背景色,并且在堆栈视图周围具有虚线边框,以便于查看帧):

enter image description here enter image description here

enter image description here enter image description here

这是我使用的代码-没有@IBoutlet连接或原型单元,因此只需添加一个表视图控制器并将该类分配给PatroTableViewController

class PatroCell: UITableViewCell {
    
    static let identifier: String = "patroCell"
    
    let horizontalStack: UIStackView = {
        let v = UIStackView()
        v.spacing = 8
        v.alignment = .center
        return v
    }()
    
    let verticalStack: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.spacing = 8
        return v
    }()
    
    let imageToView: UIImageView = {
        let v = UIImageView()
        return v
    }()
    let petName: UILabel = {
        let v = UILabel()
        v.font = UIFont.systemFont(ofSize: 16.0)
        return v
    }()
    let petDescription: UILabel = {
        let v = UILabel()
        v.font = UIFont.systemFont(ofSize: 15.0)
        v.textColor = .lightGray
        v.numberOfLines = 0
        return v
    }()

    // so we can see the frame of the horizontal stack view
    let stackOutlineView: DashedOutlineView = {
        let v = DashedOutlineView()
        return v
    }()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func commonInit() -> Void {
    
        stackOutlineView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(stackOutlineView)
        
        horizontalStack.translatesAutoresizingMaskIntoConstraints = false
        
        contentView.addSubview(horizontalStack)
        horizontalStack.addArrangedSubview(imageToView)
        imageToView.contentMode = .scaleAspectFit
        verticalStack.addArrangedSubview(petName)
        verticalStack.addArrangedSubview(petDescription)
        horizontalStack.addArrangedSubview(verticalStack)
        
        let g = contentView.layoutMarginsGuide
        
        // this will avoid auto-layout warnings
        let hsBottom = horizontalStack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0)
        hsBottom.priority = UILayoutPriority(rawValue: 999)
        
        NSLayoutConstraint.activate([
            
            // constrain horizontal stack to all 4 sides - use margins
            horizontalStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            horizontalStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            horizontalStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            
            // the bottom anchor with priority 999
            hsBottom,

            // image view should be square - 1:1 ratio
            imageToView.heightAnchor.constraint(equalTo: imageToView.widthAnchor),
            
            // image view should be 80 x 80?
            //imageToView.widthAnchor.constraint(equalToConstant: 80.0),
            
            // or, maybe, image view width should be 1/2 the width of the labels?
            imageToView.widthAnchor.constraint(equalTo: verticalStack.widthAnchor, multiplier: 0.5),
            
            // constrain the outline view
            stackOutlineView.topAnchor.constraint(equalTo: horizontalStack.topAnchor, constant: 0.0),
            stackOutlineView.leadingAnchor.constraint(equalTo: horizontalStack.leadingAnchor, constant: 0.0),
            stackOutlineView.trailingAnchor.constraint(equalTo: horizontalStack.trailingAnchor, constant: 0.0),
            stackOutlineView.bottomAnchor.constraint(equalTo: horizontalStack.bottomAnchor, constant: 0.0),
            
        ])
        
        stackOutlineView.isHidden = true
        
        // change to "if true" to see the frames
        if false {
            // so we can see the UI element frames
            imageToView.backgroundColor = .red
            petName.backgroundColor = .green
            petDescription.backgroundColor = .yellow
            petDescription.textColor = .black
            stackOutlineView.isHidden = false
        }
        
    }
    
}

class PatroTableViewController: UITableViewController {
    
    let descriptions: [String] = [
        "One-line description",
        "This is the description of the pet in this cell. It is enough text that it will cause word-wrapping.",
        "This description will be much longer... It will wrap onto many lines so we can see how the cell layout will look when the description text makes the label taller than the image view on the left. Note the differences between \".alignment = .center\" vs \".alignment = .top\""
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.register(PatroCell.self, forCellReuseIdentifier: PatroCell.identifier)
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return descriptions.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: PatroCell.identifier, for: indexPath) as! PatroCell
        
        cell.petName.text = "Dog"
        cell.petDescription.text = descriptions[indexPath.row]
        cell.imageToView.image = UIImage(named: "dog")

        if indexPath.section == 0 {
            // set horizontal stack alignment to center
            cell.horizontalStack.alignment = .center
        } else {
            // set horizontal stack alignment to top
            cell.horizontalStack.alignment = .top
        }
        
        return cell
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if section == 0 {
            return "Alignment: Center"
        }
        return "Alignment: Top"
    }
}

class DashedOutlineView: UIView {
    
    var shapeLayer: CAShapeLayer!
    
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func commonInit() -> Void {
        shapeLayer = self.layer as? CAShapeLayer
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0).cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.lineDashPattern = [8,8]
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        shapeLayer.path = UIBezierPath(rect: bounds).cgPath
    }
}