Nib-file loaded UIView in UITableViewCell does not stretch

后端 未结 4 535
囚心锁ツ
囚心锁ツ 2021-01-20 17:15

I have a UIView which is reusable via a nib/xib-file. I want to load this and fill a UITableViewCell which is to be used in a self-resizing UITableView. All with auto-layout

相关标签:
4条回答
  • 2021-01-20 17:26

    I solved it by also adding a width constraint matching the tableViews width. This is code from 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
    }
    
    0 讨论(0)
  • 2021-01-20 17:33

    It looks like the problem is that the cell created with UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: cellId) has no information about the width of the table view, so it is sizing itself based on the width of the label. Apparently the table view / cell don't force the cell's content view to take its width.

    You probably want to rework some of this cell-handling code.

    If you want to load your cell from a xib, you can skip everything with constraints. Just implement:

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

    Very important: The first top level item in the .xib file must be a UITableViewCell. Nibs are UIViews by default, delete the view in IB and drag a UITableViewCell from the object library in the lower-right of IB. Then if necessary set its subclass to a UITableViewCell subclass that you created. (You might also need to set the reuseIdentifier in IB.)

    Then in 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 
    

    You probably will want to put that "RowView0002" in a constant somewhere.

    If the "RowView0002" and the RowView class both need to be views, you should probably create a subclass of UITableViewCell. Override just init(style:resueIdentifier:) and after callingsuper` add your subviews in the code above. Hope this helps!

    0 讨论(0)
  • 2021-01-20 17:34

    I solved it by exposing a method that sets the width constraint of UITableViewCell in my custom UITableViewCell class and call it from parent UITableView cellForRowAtIndexPath method.

    Here is the code in Objective 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]
         ]];
    }
    
    0 讨论(0)
  • 2021-01-20 17:36

    A full implementation of layoutAttachAll is below.

    Some usage examples first:

     // 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)
    

    Note: myView must be added as a subview before attaching to superview using these methods. Also, all participating views must be set with translatesAutoresizingMaskIntoConstraints = false.

    The full implementation:

    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
        }
    }
    
    0 讨论(0)
提交回复
热议问题