在UITableViewCell中加载Nib文件的UIView不会拉伸

时间:2017-01-25 12:40:00

标签: ios uitableview autolayout uitableviewautomaticdimension

我有一个可通过nib / xib文件重用的UIView。我想加载它并填充一个UITableViewCell,它将用于自我调整大小的UITableView。全部都是自动布局。

大多数都运行良好,但似乎加载的UIView使用附加的约束来缩小UITableViewCell的contentView。这对于身高来说是好的,但我不希望这个宽度。

How the UITableViewCell looks 忽略下面的灰色单元格,这只是一个选定的单元格。

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cellId = "RowView0002"
    var cell = tableView.dequeueReusableCell(withIdentifier: cellId)
    if cell == nil {
        cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: cellId)

        let subView = RowView(frame: cell!.frame)

        cell!.contentView.attachViewWithConstraints(subView)
        let _ = subView.viewLoadedFromNibAttached(name: cellId)

    }

    return cell!
}

override public func viewDidLoad() {
    super.viewDidLoad()

    tableView.delegate = self
    tableView.dataSource = self
    tableView.estimatedRowHeight = 40.0
    tableView.rowHeight = UITableViewAutomaticDimension
}

extension UIView
{
    public func attachViewWithConstraints(_ view:UIView)
    {
        addSubview(view)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.layoutAttachAll(to: self)
    }

    @discardableResult
    public func viewLoadedFromNibAttached<T : UIView>(name:String) -> T? {
        guard let view = Bundle.main.loadNibNamed(name, owner: self, options: nil)?[0] as? T else {
            return nil
        }
        attachViewWithConstraints(view)
        return view
    }

    public func layoutAttachAll(to childView:UIView)
    {
        var constraints = [NSLayoutConstraint]()

        childView.translatesAutoresizingMaskIntoConstraints = false
        constraints.append(NSLayoutConstraint(item: childView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0))
        constraints.append(NSLayoutConstraint(item: childView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0))
        constraints.append(NSLayoutConstraint(item: childView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0))
        constraints.append(NSLayoutConstraint(item: childView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0))

        childView.addConstraints(constraints)
    }

在RowView0002.xib中,我已将rootviews背景设置为红色,添加了一个带有4个约束的UILabel,其边缘有一些边距,如您所见。我都试图将rootView设置为类RowView以及它的文件所有者。两个&#34;工作&#34;。

知道如何让contentView与UITableView相匹配吗?

*编辑1: 绿色是UILabel的背景。红色是nib文件的背景。运行应用程序后,View heirarcy为:UITableViewCell&gt; ContentView&gt; RowView&gt; NibFileView(红色)&gt; UILabel(绿色)

检查视图层次结构显示所有约束都按预期设置。但是,UITableViewContentView具有与所看到的总大小匹配的约束(错误):

self.width = 156.5 @ 1000

5 个答案:

答案 0 :(得分:4)

下面是layoutAttachAll的完整实现。

首先使用一些使用示例:

 // pin all edge to superview
 myView.layoutAttachAll()

 // pin all edges (to superview) with margin:
 myView.layoutAttachAll(margin: 8.0)

 // for child views: pin leading edge to superview's leading edge:
 myView.layoutAttachLeading()

 // for sibling views: pin leading edge to siblingView's trailing edge:
 myView.layoutAttachLeading(to: siblingView)

 // for sibling views: pin top edge to siblingView's bottom edge:
 myView.layoutAttachTop(to: siblingView)

注意:在使用这些方法附加到superview 之前,必须将myView添加为子视图。此外,必须使用translatesAutoresizingMaskIntoConstraints = false设置所有参与视图。

完整实施:

import UIKit

extension UIView {

    /// attaches all sides of the receiver to its parent view
    func layoutAttachAll(margin : CGFloat = 0.0) {
        let view = superview
        layoutAttachTop(to: view, margin: margin)
        layoutAttachBottom(to: view, margin: margin)
        layoutAttachLeading(to: view, margin: margin)
        layoutAttachTrailing(to: view, margin: margin)
    }

    /// attaches the top of the current view to the given view's top if it's a superview of the current view, or to it's bottom if it's not (assuming this is then a sibling view).
    /// if view is not provided, the current view's super view is used
    @discardableResult
    func layoutAttachTop(to: UIView? = nil, margin : CGFloat = 0.0) -> NSLayoutConstraint {

        let view: UIView? = to ?? superview
        let isSuperview = view == superview
        let constraint = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: view, attribute: isSuperview ? .top : .bottom, multiplier: 1.0, constant: margin)
        superview?.addConstraint(constraint)

        return constraint
    }

    /// attaches the bottom of the current view to the given view
    @discardableResult
    func layoutAttachBottom(to: UIView? = nil, margin : CGFloat = 0.0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {

        let view: UIView? = to ?? superview
        let isSuperview = (view == superview) || false
        let constraint = NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: isSuperview ? .bottom : .top, multiplier: 1.0, constant: -margin)
        if let priority = priority {
            constraint.priority = priority
        }
        superview?.addConstraint(constraint)

        return constraint
    }

    /// attaches the leading edge of the current view to the given view
    @discardableResult
    func layoutAttachLeading(to: UIView? = nil, margin : CGFloat = 0.0) -> NSLayoutConstraint {

        let view: UIView? = to ?? superview
        let isSuperview = (view == superview) || false
        let constraint = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: view, attribute: isSuperview ? .leading : .trailing, multiplier: 1.0, constant: margin)
        superview?.addConstraint(constraint)

        return constraint
    }

    /// attaches the trailing edge of the current view to the given view
    @discardableResult
    func layoutAttachTrailing(to: UIView? = nil, margin : CGFloat = 0.0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {

        let view: UIView? = to ?? superview
        let isSuperview = (view == superview) || false
        let constraint = NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: isSuperview ? .trailing : .leading, multiplier: 1.0, constant: -margin)
        if let priority = priority {
            constraint.priority = priority
        }
        superview?.addConstraint(constraint)

        return constraint
    }
}

答案 1 :(得分:0)

看起来问题是使用UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: cellId)创建的单元格没有关于表格视图宽度的信息,因此它会根据标签的宽度调整自身大小。显然,表格视图/单元格不会强制单元格的内容视图占据其宽度。

你可能想要重做一些这样的细胞处理代码。

如果要从xib加载单元格,可以使用约束跳过所有内容。只需实施:

 override func viewDidLoad() {
    //...
    let nib = UINib(nibName: "RowView0002", bundle: NSBundle.main)
    tableView.reigsterNib(nib, forCellReuseIdentifier: "RowView0002")
 }

非常重要:.xib文件中的第一个顶级项必须是UITableViewCell。默认情况下,Nib是UIViews,删除IB中的视图并从IB右下角的对象库中拖动UITableViewCell。然后,如有必要,将其子类设置为您创建的UITableViewCell子类。 (您可能还需要在IB中设置reuseIdentifier。)

然后在tableView(_:cellForRowAt IndexPath:)

guard let cell = tableView.dequeueResuableCell(withIdentifier: "RowView0002", for: indexPath) as? TheNibUITableViewSubclass else { //something went wrong, probably crash } 
cell.label.text = //...
return cell 

你可能想要把那个&#34; RowView0002&#34;在一个恒定的某个地方。

如果&#34; RowView0002&#34;并且RowView类都需要是视图,您应该创建UITableViewCell的子类。覆盖只有init(style:resueIdentifier:) and after calling super`在上面的代码中添加您的子视图。希望这有帮助!

答案 2 :(得分:0)

这是正确的layoutAttachAll

public func layoutAttachAll(to parentView:UIView)
{
    var constraints = [NSLayoutConstraint]()
    self.translatesAutoresizingMaskIntoConstraints = false
    constraints.append(NSLayoutConstraint(item: self, attribute: .left, relatedBy: .equal, toItem: parentView, attribute: .left, multiplier: 1.0, constant: 0))
    constraints.append(NSLayoutConstraint(item: self, attribute: .right, relatedBy: .equal, toItem: parentView, attribute: .right, multiplier: 1.0, constant: 0))
    constraints.append(NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: parentView, attribute: .top, multiplier: 1.0, constant: 0))
    constraints.append(NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: parentView, attribute: .bottom, multiplier: 1.0, constant: 0))
    parentView.addConstraints(constraints)
}

答案 3 :(得分:0)

我通过添加与tableViews宽度匹配的宽度约束来解决它。这是来自CustomTableViewCell的代码:

public override func layoutSubviews() {
    super.layoutSubviews()

    if let width = tableView()?.frame.width, !haveAddedWidthConstraint {
        haveAddedWidthConstraint = true
        rowView.addWidthConstraint(width: width)
    }
}

UIViewExtension:

public func addWidthConstraint(width: CGFloat) {
    let widthConstraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: width)
    widthConstraint.priority = 1000
    addConstraint(widthConstraint)
}

UITableViewCellExtension:

func tableView() -> UITableView? {
    var currentView: UIView = self
    while let superView = currentView.superview {
        if superView is UITableView {
            return (superView as! UITableView)
        }
        currentView = superView
    }
    return nil
}

答案 4 :(得分:-1)

我通过在我的自定义 UITableViewCell 类中公开了一种设置UITableViewCell宽度约束的方法并通过父UITableView cellForRowAtIndexPath 方法进行调用的方法来解决了这个问题。

这是目标C中的代码。

(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{

    YourTableViewCellType *cell = [tableView dequeueReusableCellWithIdentifier:cellId forIndexPath:indexPath];
    **[cell expandToParentSize];** // Self-sizing magic!
    
    return cell;
}

// Write below code in "YourTableViewCellType" class. "containerStack" is my UIStackView that holds all controls in the nib UITableViewCell

(void)expandToParentSize 
{    
    [NSLayoutConstraint activateConstraints:@[
        [self.containerStack.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
        [self.containerStack.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
        [self.containerStack.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
        [self.containerStack.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor]
     ]];
}