Home > Software engineering >  iOS Layout Constraint Conflict in a TableView Cell
iOS Layout Constraint Conflict in a TableView Cell

Time:03-18

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

CodePudding user response:

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.

  • Related