0

I have inherited a UIKit app where my predecessor has written all the interface code by hand. I have a view controller, a simple login screen that works just fine, but when I add it to a UINavigationController it is oddly stretched. This doesn't make a lot of sense to me.

I feel like I must be missing some simple flag, or what do I need to do to make the Navcontroller play nice with this programmatic autolayout (which is hopefully my last ever)

// this a view controller extension    
func apply(constraints: [NSLayoutConstraint]) {
        translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate(constraints)
    }

There's my handy dandy, seemingly sane utility for applying constraints. As an example, the nice little oval thing making my password textfield looks nice has these constraints set up.

// these constraints are being setup in the view controller
        emailCapsuleView.apply(constraints: [
            emailCapsuleView.topAnchor.constraint(equalTo: subBigTitle.bottomAnchor, constant: 24.0),
            emailCapsuleView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            emailCapsuleView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
            emailCapsuleView.heightAnchor.constraint(equalToConstant: Dimensions.inputFieldHeight)
        ])

Outside of a UINavigationController everything is fine, enter image description here

but in a UINavigationController everythhing is super wide and broken (simulator shot)

enter image description here

The width is not the problem here - if I use a constant for the width, my subview is still off center despite having it's centerX sets to the view controller's view's center X.

enter image description here

Maysam
  • 7,246
  • 13
  • 68
  • 106
Tom Schulz
  • 612
  • 6
  • 23
  • Do you get any warnings about constraint conflicts? What is `view`? What is its width reported as in a navigation controller? – Paulw11 Apr 19 '21 at 21:35
  • view is the viewcontroller's view. even if the width is constant it doesn't matter. you can see how everything is off center. – Tom Schulz Apr 19 '21 at 21:41
  • Well, the width of the capsule is proportional to the width of the view. It looks to me like the capsule is laid out "correctly" in a view that is wider than you want it to be, – Paulw11 Apr 19 '21 at 21:45
  • yes, so clearly the width being communicated to the viewcontroller contained in the navigation controller is wrong. furthermore even if I use a constant width it's still off center. – Tom Schulz Apr 19 '21 at 21:47
  • Well, no because you constrain the capsules center to the view's center, so if the view is wider than you want then the capsule will be "off center". What is `view`? Is it just a view controller's root view or something else? A common mistake is manually setting a view's width in `viewDidLoad`. Geometry isn't correct until `viewDidLayoutSubviews` – Paulw11 Apr 19 '21 at 21:54
  • allright then, so why does it work outside the nav controller but not in? You are chasing a red herring with the width thing here. – Tom Schulz Apr 19 '21 at 22:12
  • 1
    Can you show the following code: the navigation controller itself and how its root is added to the view, any container view controller it may be in, and this view controller in question. Just the relevant parts of course. – trndjc Apr 19 '21 at 22:27
  • Obviously the navigation controller changes the effective width of whatever `view` is; The width isn't a red herring. The view is laid out *correctly as per the constraints and the width of `view`*, it just isn't what you want. Use "Debug view hierarchy" and you will see that `view` extends off screen and your capsule is in the centre of that. The problem isn't in the code shown. – Paulw11 Apr 19 '21 at 23:19
  • Based on the ^^ comment, it sounds like `view`, which is the superview of `emailCapsuleView`, has defined either (a) it's width to extend off the bounds of `UIScreen` or has it's trailingAnchor set to something off it. (Both are definitely possible.) Please post the constraints on `view`. –  Apr 20 '21 at 00:07
  • view is just the view controllers view property. it does not have constraints on it. that view controller is added to the navigation controller when it's created. so yeah, the view controller is wrong about how wide it should be. but why ? – Tom Schulz Apr 20 '21 at 03:31

1 Answers1

-1

Configure your navigation controller and after that try like this:

set your objects:

let emailTextfield: UITextField = {
    let tf = UITextField()
    tf.backgroundColor = .white
    tf.layer.borderWidth = 1
    tf.layer.borderColor = UIColor.lightGray.cgColor
    tf.setPadding(left: 10, right: 10)// use my extension below
    tf.attributedPlaceholder = NSAttributedString(string: "Email address", attributes: [.foregroundColor: UIColor.lightGray])
    tf.layer.cornerRadius = 14
    tf.clipsToBounds = true
    tf.translatesAutoresizingMaskIntoConstraints = false
    
    return tf
}()

let passTextfield: UITextField = {
    let tf = UITextField()
    tf.backgroundColor = .white
    tf.layer.borderWidth = 1
    tf.layer.borderColor = UIColor.lightGray.cgColor
    tf.setPadding(left: 10, right: 10)// use my extension below
    tf.attributedPlaceholder = NSAttributedString(string: "Password", attributes: [.foregroundColor: UIColor.lightGray])
    tf.layer.cornerRadius = 14
    tf.clipsToBounds = true
    tf.translatesAutoresizingMaskIntoConstraints = false
    
    return tf
}()

let loginButton: UIButton = {
    let b = UIButton(type: .system)
    b.backgroundColor = .black
    b.setTitle("Save Image", for: .normal)
    b.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
    b.setTitleColor(.white, for: .normal)
    b.layer.cornerRadius = 14
    b.clipsToBounds = true
    b.translatesAutoresizingMaskIntoConstraints = false
    
    return b
}()

let subBigTitle: UILabel = {
    let l = UILabel()
    l.text = "Your Big Title"
    l.font = .systemFont(ofSize: 30, weight: .regular)
    l.textColor = .black
    l.textAlignment = .center
    l.translatesAutoresizingMaskIntoConstraints = false
    return l
}()

Now in viewDidLoad set stackView, bigTitle label and constraints:

view.addSubview(subBigTitle)
    subBigTitle.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    subBigTitle.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
    subBigTitle.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
    subBigTitle.heightAnchor.constraint(equalToConstant: 60).isActive = true
    
    let stackView = UIStackView(arrangedSubviews: [emailTextfield, passTextfield, loginButton])
    stackView.axis = .vertical
    stackView.distribution = .fillEqually
    stackView.spacing = 10
    stackView.translatesAutoresizingMaskIntoConstraints = false
    
    view.addSubview(stackView)
    stackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
    stackView.heightAnchor.constraint(equalToConstant: 170).isActive = true
    stackView.topAnchor.constraint(equalTo: subBigTitle.bottomAnchor, constant: 24).isActive = true
    stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

to add text padding to your textfield use my extension:

extension UITextField {

func setPadding(left: CGFloat, right: CGFloat){

 let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: left, height: self.frame.size.height))
 self.leftView = paddingView
 self.leftViewMode = .always

 let paddingViewRight = UIView(frame: CGRect(x: 0, y: 0, width: right, height: self.frame.size.height))
 self.rightView = paddingViewRight
 self.rightViewMode = .always
 }
}

and this is the result:

enter image description here

Fabio
  • 5,432
  • 4
  • 22
  • 24
  • So, this is inherited code. I'm trying to AVOID just rewriting the whole thing. the dude is doing some wierd stuff with his Window, too - in my decade of iOS development I have not seen anyone subclass UIWindow, for instance. I understand all that code. I'm basically doing all that. There's something going on with the uinav controller or the window. My view controller looks just fine, like yours, until i put it into this nav controller. also, my code does all the same stuff to the constraints yours does. – Tom Schulz Apr 20 '21 at 16:33
  • @TomSchulz is your navigation controller setting right? I write an extension for it https://stackoverflow.com/a/58361273/5575955 in the last part of this answer is explained how to present it from scene delegate... Take a look.... – Fabio Apr 20 '21 at 16:42