0
votes

I've been trying to follow this github tutorial, however instead of having static cells, they need to be dynamic since I don't know the height at compile time. In the tableViewController I have set the heightForRowAt to be 0 or UITableViewAutomaticDimension depending if the section is collapsed or not. Similar with the estimatedHeightForRowAtthe value is 0 or a higher value if it's expanded.

Getting to the problem now, I have subclassed the UITableViewCell and when the cellForRowAt is called it will add a subview like the following:

override func addSubview(_ view: UIView) {
        guard view != nil else {return}

        for view in self.subviews {
            view.removeFromSuperview()
        }

        super.addSubview(view)
        //view.applyLeadingAndTrailingPinConstraint(toSuperview: 0)
        //view.applyTopAndBottomPinConstraint(toSuperview: 0)

        let addedView = self.subviews[0]

        let bottom = NSLayoutConstraint(item: addedView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
        bottom.priority = 750
        bottom.isActive = true
        NSLayoutConstraint(item: addedView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: addedView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: addedView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: addedView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: addedView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0).isActive = true

        //NSLayoutConstraint.reportAmbiguity(v: self)
        if addedView.hasAmbiguousLayout {
            print("Horizontal: \(addedView.constraintsAffectingLayout(for: .horizontal))")
            print("Vertical: \(addedView.constraintsAffectingLayout(for: .vertical))")
            print("Trace: \(addedView.value(forKey: "_autolayoutTrace"))")
        }
        //addedView.exerciseAmbiguityInLayout()

        //print("self: \(self.frame), subview: \(self.subviews[0].frame)")
    }

As you may see, the problem is that there's an ambiguity with the constraints, hence the hasAmbigousLayout. When the view is collapsed there is no error. However when I expand the view, which has also a height constraint, the ambiguity happens. The following is the output of the 3 print statements inside hasAmbigousLayout:

Horizontal: []
Vertical: [<NSLayoutConstraint:0x60800028ea10 UIView:0x7fcfe856a790.height == 100   (active)>]
Trace: Optional(
•ViraVira_Info.WellnessCell:0x7fcfe8859a00'cell', MISSING HOST CONSTRAINTS
|   *UIView:0x7fcfe856a790- AMBIGUOUS LAYOUT for UIView:0x7fcfe856a790.minX{id: 776}, UIView:0x7fcfe856a790.minY{id: 771}, UIView:0x7fcfe856a790.Width{id: 778}

Legend:
    * - is laid out with auto layout
    + - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES
    • - layout engine host)

I have tried to lower the bottom constraint priority as sugested in this question. Also I have tried many other sites, which I sadly lost track of. The result in the app is that the view does not even show up, since it should have a red background, also when i try to print the frame of the subview, the width is 0 which seems odd.

I probably made a silly mistake with the constraint but after 2 days trying I still can't figure it out.

Thanks for your time -Jorge

1

1 Answers

0
votes

There are several issues to note (and may be part of the problem):

  • override func addSubview(_ view: UIView) -> I don't think thats the right place for your code. I develop several years now iOS and there was never a reason to override that method. View layout should be done in Interface Builder or in the parent view.
  • guard view != nil else {return} -> should give you a warning, because view is not an optional (UIView?), so it can't be nil. This check does simply nothing useful.
  • for view in self.subviews { view.removeFromSuperview() } super.addSubview(view) -> This will remove every previously added view, the last added view will win the race, thats completely unexpected by the caller of addSubview - If a cell adds more than one subview this will cause trouble and headache.
  • let addedView = self.subviews[0] -> must be equal view, so why not using view in the later code?
  • 6 * NSLayoutConstraint -> A view is normally positioned by 4 constraints, 2 vertical and 2 horizontal - I think you should remove the last two (center constraints) - That may be your problem.

Try to refactor your code...