Chapter 20, strange error, Detected a case where constraints ambiguously suggest a height of

I have run across this warning and can see it is the third element, date picker that is failing in the table view cell. The picker will not activiate on taps from the view cell…I am assuming because of the warning but perhaps I have done something else wrong…any one help me out here?

2018-01-15 14:38:30.215131-0800 Checklists[11242:883617] [Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell’s content view. We’re considering the collapse unintentional and using standard height instead.

Hi @kary993,
The only way someone can help is if they can see the code for this.

cheers,

Jayant

ok, here is the ItemDetailViewController.switft file I am working with…

import UIKit

protocol AdditemControllerDelegate: class {
func itemDetailViewControllerDidCancel(_ controller: ItemDetailViewController )
func itemDetailViewController(_ controller: ItemDetailViewController, didFinishAdding item: ChecklistItem )
func itemDetailViewController(_ controller: ItemDetailViewController, didFinishEditing item: ChecklistItem )
}

class ItemDetailViewController: UITableViewController, UITextFieldDelegate {

@IBOutlet weak var textField: UITextField!
@IBOutlet weak var doneBarButton: UIBarButtonItem!
weak var delegate: AdditemControllerDelegate?
@IBOutlet weak var shouldRemindSwitch: UISwitch!
@IBOutlet weak var dueDateLabel: UILabel!
var dueDate = Date()
var datePickerVisible = false
@IBOutlet weak var datePickerCell: UITableViewCell!
@IBOutlet weak var datePicker: UIDatePicker!

func updateDueDateLabel() {
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .short
    dueDateLabel.text = formatter.string(from: dueDate)
}

@IBAction func cancel() {
    delegate?.itemDetailViewControllerDidCancel(self)
}

@IBAction func done() {
    if let item = itemToEdit {
        item.text = textField.text!
        
        item.shouldRemind = shouldRemindSwitch.isOn
        item.dueDate = dueDate
        
        delegate?.itemDetailViewController(self, didFinishEditing: item )
    } else {
        let item = ChecklistItem()
        item.text = textField.text!
        item.checked = false
        
        item.shouldRemind = shouldRemindSwitch.isOn
        item.dueDate = dueDate
        
        delegate?.itemDetailViewController( self, didFinishAdding: item )
    }
}

@IBAction func dateChanged(_ datePicker:UIDatePicker ) {
    dueDate = datePicker.date
    updateDueDateLabel()
}

func hideDatePicker() {
    if datePickerVisible {
        datePickerVisible = false
        
        let indexPathDateRow = IndexPath(row: 1, section: 1)
        let indexPathDatePicker = IndexPath(row: 2, section: 1)
        
        if let cell = tableView.cellForRow(at: indexPathDateRow) {
            cell.detailTextLabel!.textColor = UIColor.black
        }
        tableView.beginUpdates()
        tableView.reloadRows(at: [indexPathDateRow], with: .none)
        tableView.deleteRows(at: [indexPathDatePicker], with: .fade)
        tableView.endUpdates()
    }
}

func showDatePicker() {
    datePickerVisible = true
    let indexPathDateRow = IndexPath( row: 1, section: 1 )
    let indexPathDatePicker = IndexPath( row: 2, section: 1 )
    
    if let dateCell = tableView.cellForRow( at: indexPathDateRow ) {
        dateCell.detailTextLabel!.textColor = dateCell.detailTextLabel!.tintColor
    }
    
    tableView.beginUpdates()
    tableView.insertRows( at: [indexPathDatePicker], with: .fade )
    tableView.reloadRows( at: [indexPathDateRow], with: .none )
    tableView.endUpdates()
    
    datePicker.setDate(dueDate, animated: false )
}

var itemToEdit: ChecklistItem?

override func viewDidLoad() {
    super.viewDidLoad()

    navigationItem.largeTitleDisplayMode = .never
    
    if let item = itemToEdit {
        title = "Edit Item"
        textField.text = item.text
        doneBarButton.isEnabled = true
        shouldRemindSwitch.isOn = item.shouldRemind
        dueDate = item.dueDate
    }
    
    updateDueDateLabel()
}

// MARK:- tableView overrides
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath ) -> UITableViewCell {
    if indexPath.section == 1 && indexPath.row == 2 {
        return datePickerCell
    } else {
        return super.tableView( tableView, cellForRowAt: indexPath )
    }
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int ) -> Int {
    if section == 1 && datePickerVisible {
        return 3
    } else {
        return super.tableView( tableView, numberOfRowsInSection: section )
    }
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath ) -> CGFloat {
    if indexPath.section == 1 && indexPath.row == 2 {
        return 217
    } else {
        return super.tableView( tableView, heightForRowAt: indexPath )
    }
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath ) {
    tableView.deselectRow( at: indexPath, animated: true )
    textField.resignFirstResponder()
    print( "We are here didSelectRowAt")
    
    if indexPath.section == 1 && indexPath.row == 1 {
        if !datePickerVisible {
            showDatePicker()
        } else {
            hideDatePicker()
        }
    }
}

override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath ) -> IndexPath? {
    print( "We are here willSelectRowAt")
    if indexPath.section == 1 && indexPath.row == 1 {
        return indexPath
    } else {
        return nil
    }
}

override func tableView(_ tableView: UITableView, indentationLevelForRowAt indexPath: IndexPath ) -> Int {
    var newIndexPath = indexPath
    print( "We are here indentationLevelForRowAt section: \(indexPath.section) row: \(indexPath.row)")
    if indexPath.section == 1 && indexPath.row == 2 {
        
        newIndexPath = IndexPath( row: 0, section: indexPath.section )
    }
    
    return super.tableView( tableView, indentationLevelForRowAt: newIndexPath )
}

override func viewWillAppear(_ animated: Bool ) {
    super.viewWillAppear(animated)
    textField.becomeFirstResponder()
}

func textField(_ textField: UITextField,
               shouldChangeCharactersIn range: NSRange,
               replacementString string: String) -> Bool {
    let oldText = textField.text!
    let stringRange = Range( range, in: oldText )!
    let newText = oldText.replacingCharacters( in: stringRange, with: string )
    doneBarButton.isEnabled = !newText.isEmpty
    return true
}

func textFieldDidBeginEditing(_ textField: UITextField) {
    hideDatePicker()
}

}

This seems to indicate that you might not have enough constraints (perhaps on the date picker) in order for iOS to be figure out the height of the table view cell. This would normally happen if you forgot to set up the relevant height or top and bottom constraints. You might want to check that.

However, I don’t know if that is actually the issue you’re facing. As @jayantvarma mentioned, we really need to see the working code in order to figure out what is going on. However, posting part of a Swift file isn’t going to help much either since it’s easier to actually run the app and see what is going on :slight_smile: So if you can upload your full project as a ZIP file somewhere and then provide a link to that here, I (or somebody else) can take a look and help you.

@kary993,
In case you want to debug this yourself, then you would have to consider commercial tools that help you resolve these issues. Here’s one that is quite reputed and well known ( https://revealapp.com/blog/constraints-and-transforms.html ) from Reveal. Here’s the article on the site ( https://www.raywenderlich.com/97174/reveal-tutorial-live-view-debugging )

cheers,

Jayant

Kary993Checklist.zip (120.7 KB)

I checked the contraints and have four on the date picker.

I have attached the entire project here, hopefully it is there…I appreciate the help as I am still learning all this given I am ancient, programming in the 70’s :wink:

Kary

Jayant,

Thank you for the advice. I just looked at that and it seems interesting, perhaps beyond my knowledge to date but would be helpful once I master a few more things :wink: I have been playing with layouts on the side quite a bit and just getting use to the concepts of these contraints.

Cheers!

Kary

No worries @kary993.

from your previous comment, The UI layer is easy but takes a bit of time to get used to, add to that asynchronous can be a challenge. If you are in your 70’s that’s great, would love to know how you are progressing with your adventure in programming.

cheers,

Jayant

well, my background is undergrad and grad computer science. Started PhD but didn’t really need that for wht i was doing in my career. I have extensively programmed in Fortran (all of them), C, C++, Java, JavaScript, PHP, Ada (not really around anymore but a full environment unlike the others), Python, Tck/Tk, all UNIX shell scripting…you get the picture. I spend most of my time these days managing medium to larger scale IT shops that also do development. Development, ERP/CRM/MRP and all the things inbetween thst make a business run. So I have a solid understanding of constructs as well as MVC but while I developed something like this in the 90’s for Cray Research/Sun Microsystems ( you probably don;t now the CS6400?), iOS is much more comprehensive though the thoughts are the same just deeper. Asynchronous really not a problem for me. I developed software fo near real time data acquisition for communication satellites so that and MVC is well understood, just need to know the callbacks (yes old term) for the various delegates and segues, etc. I enjoy frameworks just takes a lot of time to learn but once learned things go fast!

I started coding back in the '80s - so I think I kind of understand where you’re coming from :smiley:

I took a look at the project and there were two issues. The first one, the warning you were seeing, did not have anything to do with the second one, the date picker not appearing.

Here’s what was happening:

  1. The table view for that view controller had the row height set to Automatic. Since the code calls the method on the superclass to get the cell height, and the tableview on the storyboard did not have an explicit row height, you were seeing the warning that you saw. You can fix this by setting the table view’s default row height to 44 or something similar. This should not cause any major issues would be my feeling.

  2. The tableview on your storyboard had its Selection property (you can access this via the Attributes inspector) set to No Selection. This is why you weren’t getting the date picker. No Selection means that you cannot select rows on the table view. So none of your taps on the rows had any effect. That property should be set to to Single Selection. Once that change was made, the date picker showed up fine :slight_smile:

Ok thank you for the professional advice that repaired that but now a crash, I have a different problem now that I will investigate and repair so I may learn. Otherwise will come back for advise :wink:

I appreciate the quick and kind responses!

Kary

Set a breakpoint on line 34 and check the values of the relevant variables if you want to fix this on your own …

If not, you can check my answer below by expanding the “Solution” to see a possible issue based on just the crash error message …

Solution

It is possible that you forgot to hook up the outlet for dueDateLabel. Generally, when you get a crash related to nil while unwrapping an Optional, this is the case :slight_smile:

Of course, it is also possible that the issue is actually with dueDate itself but the former is the more likely scenario.

You guys are really great! I learned all of computer science the hard way back in the day…languages, patterns, object orientation, etc…but few ever helped like this. Something close to may heart. I have mentored and helped and love doing it today when I can. I’ll say under a 100 but maybe more than a 100 in my career. The world today with current mediums can help millions like you are doing.

It is a shame the knowledge I have was not passed off to you all so you could move even faster, but such is life! You are doing quite well!

I will work on this and if needed I will look at your solution. Thank you!

Kary

Thank you for the help, I had few other problems as well but it finally works! I did peak at your solution, it was the case and a good tip when unwrapping results in nil !

Moving forward to My Locations!

Thank you for the help again!

Kary

Glad to hear that you were able to resolve the issues and are now on to the next project :slight_smile: Have fun with the coding!

Thank you, love the exercises at the end. One question. If one checks an item as done before the notification date/time expires, the notification still fires, correct?

Yes, it does :wink: And that’s actually a bug, if you look at it in real-world terms. So, if you want an additional exercise, you might want to look at adding the functionality to remove an existing notification via the configureCheckmark method …

Will do thanks for the confirmation!

This topic was automatically closed after 166 days. New replies are no longer allowed.