2

I'm getting a layout constraint error for a UILable in a UITableViewCell. My app is a to do list, and I have a UITableView that contains UITableViewCells of varying heights. If I drag a cell down to the bottom slowly and the page starts to scroll, I get the error. see video of error condition

Here is the error I am getting:

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2022-03-12 11:18:28.445423-0800 Action[83734:5205929] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600003361590 V:|-(14)-[UILabel:0x1541a05d0]   (active, names: '|':UITableViewCellContentView:0x15419ef60 )>",
    "<NSLayoutConstraint:0x600003361680 V:[UILabel:0x1541a05d0]-(14)-|   (active, names: '|':UITableViewCellContentView:0x15419ef60 )>",
    "<NSLayoutConstraint:0x600003361bd0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x15419ef60.height == 0   (active)>"
)

I set a symbolic breakpoint as suggested and get this additional info (4th line has "AMBIGUOUS LAYOUT"):

|   |   |   |   |   |   |   *<UILayoutGuide: 0x600003f7c380 - "UIViewSafeAreaLayoutGuide", layoutFrame = {{0, 94}, {375, 684}}, owningView = <UIView: 0x132006ea0; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x600000679480>>>
|   |   |   |   |   |   |   *UIView:0x132007010
|   |   |   |   |   |   |   |   *UITableView:0x13300c000
|   |   |   |   |   |   |   |   |   *<UIFocusContainerGuide: 0x60000396c4b0 - "UITableViewContentFocusContainerGuide", layoutFrame = {{0, 0}, {375, 684}}, owningView = <UITableView: 0x13300c000; frame = (0 0; 375 684); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000008dc5a0>; layer = <CALayer: 0x6000006597e0>; contentOffset: {0, 0}; contentSize: {375, 2783.9999949137377}; adjustedContentInset: {0, 0, 0, 0}; dataSource: <Action.ProjectController: 0x130e085e0>>>- AMBIGUOUS LAYOUT for UIFocusContainerGuide:0x60000396c4b0'UITableViewContentFocusContainerGuide'.minX{id: 3169}, UIFocusContainerGuide:0x60000396c4b0'UITableViewContentFocusContainerGuide'.minY{id: 3170}, UIFocusContainerGuide:0x60000396c4b0'UITableViewContentFocusContainerGuide'.Width{id: 3171}, UIFocusContainerGuide:0x60000396c4b0'UITableViewContentFocusContainerGuide'.Height{id: 3172}
|   |   |   |   |   |   |   |   |   Action.ActionCellView:0x12f0a1600'ReusableCell'
|   |   |   |   |   |   |   |   |   |   _UISystemBackgroundView:0x12de973f0
|   |   |   |   |   |   |   |   |   |   |   UIView:0x12de95f90
|   |   |   |   |   |   |   |   |   |   •UITableViewCellContentView:0x12de962a0
|   |   |   |   |   |   |   |   |   |   |   *UIButton:0x12de96440
|   |   |   |   |   |   |   |   |   |   |   |   _UISystemBackgroundView:0x12de968e0
|   |   |   |   |   |   |   |   |   |   |   |   UIImageView:0x12de96700
|   |   |   |   |   |   |   |   |   |   |   *UILabel:0x12de96aa0
|   |   |   |   |   |   |   |   |   |   _UITableViewCellSeparatorView:0x12de97080
|   |   |   |   |   |   |   |   |   |   _UITableViewCellSeparatorView:0x12de99c70
|   |   |   |   |   |   |   |   |   |   _UITableViewCellSeparatorView:0x1309081d0

Here is how I have set up the table view in my controller's viewDidLoad:

tableView.register(UINib(nibName: K.Action.cellNibName, bundle: nil), forCellReuseIdentifier: K.Action.cellIdentifier)
tableView.dataSource = self
tableView.dragDelegate = self
tableView.dropDelegate = self
tableView.delegate = self
tableView.showsVerticalScrollIndicator = false
        

Here is the UITableViewDataSource implementation:

extension ProjectController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return tableView.rowHeight
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let project = model.projects.data[projectId!] {
            return project.actionIds.count
        }
        
        return 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: K.Action.cellIdentifier, for: indexPath) as! ActionCellView
        
        if let project = model.projects.data[projectId!] {
            let actionId = project.actionIds[indexPath.row]
            let action = model.actions.data[actionId]!
            
            cell.titleLabel.text = action.title
            cell.delegate = self
            cell.actionId = actionId
            
            if action.scheduled != nil {
                cell.label = action.scheduled!.toString()
            }
        }
        
        return cell
    }
}

I've also implemented UITableViewDragDelegate:

extension ProjectController: UITableViewDragDelegate {
    func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        let dragItem = UIDragItem(itemProvider: NSItemProvider())
        return [ dragItem ]
    }
    
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let project = model.projects.data[projectId!]!
        let actionId = project.actionIds[sourceIndexPath.row]
        model.moveAction(actionId, inProject: projectId!, toRank: destinationIndexPath.row)
    }
}

Screenshot of Table View constraints

Screenshot of Table View cell Label constraints

Josh R
  • 111
  • 1
  • 9
  • The initial error is _not_ ambiguous layout. It's a conflict. You need to stop and fix that before you worry about anything else. – matt Mar 12 '22 at 20:37
  • 1
    Okay so you've tried to combine the built in `titleLabel` with custom interface in your cell. You mustn't do that. Use a purely custom cell and give the label a different name. – matt Mar 12 '22 at 20:41
  • @matt thanks for the response. I renamed titleLabel to primaryLabel, but it didn't fix the issue. Instead I set a lower priority for the label bottom constraint, which worked. – Josh R Mar 12 '22 at 23:16

1 Answers1

2

I was able to solve this issue by changing the priority of the label's bottom anchor from 1000 to 999, as suggested in https://stackoverflow.com/a/36949580/10426153

However, it results in a scenario where if I drag a cell down so that the page scrolls, and then drag back up, the cell that is below where the drag cell was originally located has an expanded and incorrect height. In this scenario, it looks like the label's bottom anchor gets de-prioritized (as expected with the lower priority), but results in an odd height.

I think @matt was indicating there is another root cause that could be fixed, however, I'm not quite sure what that is.

Josh R
  • 111
  • 1
  • 9
  • That is true and I was going to suggest it, but it doesn't really get at the other issue I was pointing out. – matt Mar 12 '22 at 23:19
  • @matt sorry, which issue are you referring to? Not sure I'm following – Josh R Mar 12 '22 at 23:25
  • @matt can you please explain the issue a little more? – Josh R Mar 19 '22 at 21:27
  • Have you solved your problem or not? – matt Mar 19 '22 at 22:11
  • @matt I solved the layout constraint error, but now there is some glitchy UI behavior where the neighboring cell to the one that is being dragged changes height (makes it too big) and then once the cell is dropped, the height of the neighbor changes back. It's an odd user experience. – Josh R Mar 21 '22 at 04:03