skip to Main Content

In a nut shell. I am trying to add constraints to resize a date picker and text view. Without the text view the date picker does what I want it to do (move to the top and centred), I add the text view it as soon as I add any for of constraint, when I run it I get this. What am I missing or getting wrong

These are the constraints

This is what it look liked

But this is what I want it to look like

2

Answers


  1. You didn’t specify compatibility version but let’s assume that it is iOS 14 since you want to use compact date picker.

    You can change Preferred Style to .compact in storyboard to preview how it will look like.

    enter image description here

    But, ups… 😦 now storyboard warns you that there is problem with your layout. More specifically, date picker doesn’t know how heigh it should be.

    enter image description here

    There are several ways how to fix problem with ambiguous height of view. But for some reason, auto-layout doesn’t work for this view until you manually set its height with height constraint. 🤯

    enter image description here


    "Auto-layout" solution

    But, there is also one "hacky" solution which I’ve just created. But to make it work we need to embed out views inside vertical UIStackView (ideally with center alignment)

    enter image description here

    and our date picker needs to have required vertical content hugging priority

    enter image description here

    Now start with creating custom subclass of UIDatePicker and specify its intrinsicContentSize to height of first subview of first subview (private hierarchy, but 🤷🏻‍♂️)…

    enter image description here

    So create this subclass and don’t forget to set this subclass in storyboard.

    final class CompactDatePicker: UIDatePicker {
    
        override var intrinsicContentSize: CGSize {
            guard
                let compactDatePickerViewFrame = subviews.first?.subviews.first?.frame,
                compactDatePickerViewFrame.height != 0
            else { return super.intrinsicContentSize }
            return compactDatePickerViewFrame.size
        }
    
    }
    

    Note that I also set its width to this subview’s width so it also works when you have for example date picker in vertical stack view with center alignment.

    enter image description here

    Now one more thing you have to do is to invalidate its intrinsic content size in view controller’s viewDidLayoutSubviews

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        datePicker.invalidateIntrinsicContentSize()
    }
    

    I feel like that this new compact date picker is something which Apple should definitely change, but 🍎…

    Login or Signup to reply.
  2. Apple provided two new date picker appearances (inline and compact), changed the default appearance to one of these new appearances (compact) and did not provide a way to select the old appearance (wheels) until a later iOS version (at 13.4 through UIDatePicker.preferredDatePickerStyle). But what is really causing your problem here is that Apple seemingly forgot to implement intrinsicContentSize for these new appearances which is a problem if your constraints fail at positively determining a height. Robert Dresler’s answer is on the right track (trying to bring back a useful intrinsicContentSize implementation) but it relies on knowledge of the internal structure of these views. As such

    • it only seems to work for compact date pickers (the assumption being that the first subview of the first subview provides the correct intrinsic content size)
    • it can break at any time should apple decide to change the internal structure of these views.

    Until Apple finally fixes theses controls I had great success with the following

    class FixedDatePicker: UIDatePicker {
    
        override var intrinsicContentSize: CGSize {
        
            let host = UIView()
            host.translatesAutoresizingMaskIntoConstraints = false
        
            let probe = UIDatePicker()
            probe.translatesAutoresizingMaskIntoConstraints = false
        
            probe.datePickerMode = self.datePickerMode
        
            if #available(iOS 13.4, *) {
                probe.preferredDatePickerStyle = self.preferredDatePickerStyle
            }
                
            let top = NSLayoutConstraint(item: host, attribute: .top, relatedBy: .equal, toItem: probe, attribute: .top, multiplier: 1, constant: 0)
            let bottom = NSLayoutConstraint(item: host, attribute: .bottom, relatedBy: .equal, toItem: probe, attribute: .bottom, multiplier: 1, constant: 0)
        
            let left = NSLayoutConstraint(item: host, attribute: .left, relatedBy: .equal, toItem: probe, attribute: .left, multiplier: 1, constant: 0)
            let right = NSLayoutConstraint(item: host, attribute: .right, relatedBy: .equal, toItem: probe, attribute: .right, multiplier: 1, constant: 0)
        
            host.addSubview(probe)
            host.addConstraints([left, right, top, bottom])
        
            return host.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        }
    }    
    

    Same idea as Robert (provide a better intrinsicContentSize implementation) only mine does not rely on any specific knowledge of the view’s internal structure and uses UIView.systemLayoutSizeFitting to calculate a size.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search