0

While working through a Udemy course, I'm trying to make a simple log in / sign up interface. In my first view controller I have a vertical stack view containing a label, a segmented control, and a container view. The segmented control can be either "Log In" or "Sign Up". I want to load the appropriate view controller when the segmented control changes.

Top Level View Controller

My LogInViewController has a vertical stack view with a text field for username, another for password, and a Log In button. My SignUpViewController has a vertical stack view with text fields for email address, username, password, and password confirmation followed by a Sign Up button.

My first view controller looks like this:

import UIKit

class ViewController: UIViewController, LogInViewDelegate, SignUpViewDelegate
{
    @IBOutlet weak var logInSignUpControl: UISegmentedControl!
    @IBOutlet weak var containerView: UIView!

    var logInVC: LogInViewController?
    var signUpVC: SignUpViewController?
    var activeVC = 0

    override func viewDidLoad()
    {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        initializeCustomControllers()
    }

    func initializeCustomControllers()
    {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)

        logInVC = storyboard.instantiateViewController(withIdentifier: "LogInViewController") as? LogInViewController
        signUpVC = storyboard.instantiateViewController(withIdentifier: "SignUpViewController") as? SignUpViewController

        logInVC?.delegate = self
        signUpVC?.delegate = self

        logInVC?.willMove(toParent: self)
        logInVC?.view.frame = containerView.bounds
        containerView.addSubview(logInVC!.view)
        addChild(logInVC!)
        logInVC?.didMove(toParent: self)
    }

    func swapCustomController(from: UIViewController,to: UIViewController)
    {
        from.view.removeFromSuperview()
        from.removeFromParent()

        to.willMove(toParent: self)
        to.view.frame = containerView.bounds
        containerView.addSubview(to.view)
        addChild(to)
        to.didMove(toParent: self)
     }

    @IBAction func logInSignUpControlTapped(_ sender: Any)
    {
        switch logInSignUpControl.selectedSegmentIndex
        {
        case 0:
            if activeVC == 1
            {
                swapCustomController(from: signUpVC!, to: logInVC!)
                activeVC = 0
            }
        case 1:
            if activeVC == 0
            {
                swapCustomController(from: logInVC!, to: signUpVC!)
                activeVC = 1
            }
        default:
            break
        }
    }

    func logInAttempted(error: Error?)
    {
        if error == nil
        {
            // segue
            print("log in successful")
        }
        else
        {
            showAlert(title: "Log In error",
                      message: error?.localizedDescription ?? "Unknown Error")
        }
    }

    func signUpAttempted(error: Error?)
    {
        showAlert(title: "Sign Up Error",
                  message: error?.localizedDescription ?? "Unknown Error")
    }
}

The problem is that, when either the log in or sign up views are loaded into the container view, the fields and buttons are not usable from the simulator. I can't tap into the text fields and the buttons are unresponsive.

I tried adding a call to makeFirstResponder() to the first text field, which allowed me to enter text, but the buttons are still unusable.

Thanks in advance for any help.

Patrick
  • 261
  • 3
  • 16
  • Have you confirmed solutions in: https://stackoverflow.com/questions/49134601/buttons-inside-container-view-does-not-work/49134788 – DevKyle Dec 21 '19 at 19:26
  • It looks very similar, but changing the size of the container view didn't help. – Patrick Dec 22 '19 at 18:18

1 Answers1

1

The main issue here is probably in the words "vertical stack view". A stack view can operate correctly only if it is sized by autolayout constraints. Yours might not be correctly pinned to its superview.

Also, I don't see any code where you grapple with the possibility that the container view might be resized after you assign its bounds to the frame of the child view.


Also, this might not matter here, but in general please note that you are doing this dance incorrectly:

    logInVC?.willMove(toParent: self)
    logInVC?.view.frame = containerView.bounds
    containerView.addSubview(logInVC!.view)
    addChild(logInVC!)
    logInVC?.didMove(toParent: self)

The correct commands in the correct order is:

    addChild(logInVC!)
    logInVC!.view.frame = containerView.bounds
    containerView.addSubview(logInVC!.view)
    logInVC!.didMove(toParent: self)

In general problems with a child view controller view not responding, it is either because the outlets are not hooked up or because the view controller has been allowed to fall on the floor leaving the view with no one to talk to.

However, in your case it might well be because the buttons are ending up outside the bounds of their superview because the stack view is not configured correctly. (You can use the View Debugger to confirm that hypothesis.) A view outside of its superview is visible but not touchable.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks for the feedback on adding the first child view. I'm not sure what you mean by "A stack view can operate correctly only if it is sized by autolayout constraints." The only constraints I have for the top level vertical stack view are "Safe Area.Trailing = Stack View.Trailing + 20", "Stack View.Top = Safe Area.Top + 40", "Stack View.Center X = centerX", and "Stack View.Leading = Safe Area.Leading + 20". I updated the container view to take up the remainder of the screen, but the Log In fields and button are still unresponsive. – Patrick Dec 22 '19 at 18:13
  • I just looked up how to debug the view hierarchy and it does appear that my Log In view is outside the top level stack view. I made the container view take up the rest of the screen in the storyboard, so I don't know why it would be shrunken down to almost nothing when running. – Patrick Dec 22 '19 at 18:26
  • Because you have not done anything to give your stack view any height. – matt Dec 22 '19 at 21:05
  • Thanks. I added a constraint for "Stack View.Bottom = Safe Area.Bottom", set the stack view Distribution to "Fill", and the content view vertical Content Hugging Priority to 750 and it works. I'm accepting this as the answer. – Patrick Dec 23 '19 at 17:15
  • Yes, that’s the first thing in my answer. The stack view must be pinned to its superview. But the other stuff I said is also worth attending to. – matt Dec 23 '19 at 20:01