2

I wanted to test some Swift code but I found a problem I never saw before. When I launch the application on the iPhone simulator or on a real device, for some reason the views are not being displayed... I only did two things just after creating the project:

  1. Write some code on the AppDelegate file to create the window and then choose the initial controller.
  2. Create a simple UITextView on the viewDidLoad method of that controller

The only thing I'm getting is a full white screen, without any text on the center as it was supposed to be. I used a print("Called") to verify that viewDidLoad is being executed, and in fact, it is so... Where is the problem?

AppDelegate.swift:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = window ?? UIWindow()
        window?.backgroundColor = UIColor.white

        window?.rootViewController = ViewController()

        window?.makeKeyAndVisible()

        return true
    }

}

extension CGRect {

    init(_ x: CGFloat, _ y: CGFloat, _ w: CGFloat, _ h: CGFloat) {
        self.init(x: x, y: y, width: w, height: h)
    }

}

ViewController.swift:

class ViewController: UIViewController {

    override func viewDidLoad() {

        print("Called")

        let tv = UITextView()
        tv.text = "Hola Mundo"
        tv.sizeToFit()
        tv.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(tv)

        NSLayoutConstraint.activate([
            tv.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            tv.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
        ])

    }

}
Jaime_mc2
  • 673
  • 5
  • 18

2 Answers2

3

The view in the opening question is onscreen you just can't see it because its height and width have been compressed to nothing by Auto Layout. There are a number of ways one might change this and setting isScrollEnabled to false is the simplest (see here).

Centred and self-sizing text view (no scrolling)

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tv = UITextView()
        tv.text = "Hola Mundo"

        tv.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tv)

        // disable scrolling so that view is not allowed to collapse
        tv.isScrollEnabled = false

        // position view at centre of screen
        tv.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        tv.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

    }

}

Text view (scrolling) same size as view (excluding top status bar)

Otherwise if you want a scrolling text view then one option is to anchor the text view in a way that prevents it from collapsing in on itself, e.g.:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
   
        let tv = UITextView()
        tv.text = "Hola Mundo"
        tv.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tv)
        
        // anchor view to top, bottom, left and right of view
        tv.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
        tv.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
        tv.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        tv.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

    }


}

and text beyond the size of the frame will scroll (as per default behaviour).

Centred text view, sized proportional to view (scrolling)

Or, providing the text view with a proportional size is a way to keep the text view centred and at the same time scrollable.

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tv = UITextView()
        tv.text = "Hola Mundo"
        
        tv.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tv)

        // anchor view to centre of screen
        tv.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        tv.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        
        // set height and width relative to superview height and width
        tv.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5).isActive = true
        tv.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75).isActive = true
        
    }
    
}

Explanation

The reason these work is because of the way in which Auto Layout works with views. In short, a view with scroll enabled has no resistance to being allowed to collapse, whereas one without it has resistance to being compressed. Auto Layout is all about things being allowed to expand and shrink based on the priorities (and anchors) given to them.

Community
  • 1
  • 1
sketchyTech
  • 5,746
  • 1
  • 33
  • 56
  • I don't think it answers the question at all – Ivens Denner Feb 08 '17 at 16:41
  • The reason this works is because of the way in which Auto Layout works with views. In short, a view with scroll enabled has no resistance to being allowed to collapse, whereas one without it has resistance to being compressed. Auto Layout is all about things being allowed to expand and shrink based on the priorities given to them. The view in the opening question is onscreen you just can't see it because its height and width have been compressed to nothing by Auto Layout. There are a number of ways one might change this and setting `isScrollEnabled` is the simplest. – sketchyTech Feb 08 '17 at 16:52
  • Oh, I got it. Maybe you should add this explanation to your answer. – Ivens Denner Feb 08 '17 at 17:02
  • Done. Thanks for your input. – sketchyTech Feb 08 '17 at 18:05
1

UPDATED

if your going to be programatically adding constraints I have a functions which is an extension of UIView that will make this processs alot easier. add this to your code:

extension UIView {

    func anchor(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
        translatesAutoresizingMaskIntoConstraints = false

        var anchors = [NSLayoutConstraint]()

        if let top = top {
            anchors.append(topAnchor.constraint(equalTo: top, constant: topConstant))
        }

        if let left = left {
            anchors.append(leftAnchor.constraint(equalTo: left, constant: leftConstant))
        }

        if let bottom = bottom {
            anchors.append(bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant))
        }

        if let right = right {
            anchors.append(rightAnchor.constraint(equalTo: right, constant: -rightConstant))
        }

        if widthConstant > 0 {
            anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
        }

        if heightConstant > 0 {
            anchors.append(heightAnchor.constraint(equalToConstant: heightConstant))
        }

        anchors.forEach({$0.isActive = true})

        return anchors
    }
}

Then to center your tv view update your code to look like this:

class ViewController: UIViewController {

    override func viewDidLoad() {

        print("Called")

        view.backgroundColor = .red

        let tv = UITextView()
        tv.text = "Hola Mundo"
        self.view.addSubview(tv)


        _ = tv.anchor(view.centerYAnchor, left: nil, bottom: nil, right: nil, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 75, heightConstant: 25)

        tv.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    }

}
Kenny Gunderman
  • 490
  • 6
  • 18