Search code examples
swiftuiviewuiscrollviewuikituibutton

UIButton and TextEdit inside of UIView inside of UIScroll View


I've got a UIButton and a couple of TextEdit's in a UIView. UIView itself is in a UIScrollView. Somehow UIButton can not be pressed in the structure I mentioned. Look at the following code:

    private lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.showsVerticalScrollIndicator = true
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.backgroundColor = .systemBrown
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        return scrollView
    }()

    private lazy var contentView: UIView = {
        let contentView = UIView()
        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentView.backgroundColor = .yellow
        contentView.isUserInteractionEnabled = true
        return contentView
    }()

    private lazy var logInButton: UIButton = {
        let button = UIButton(type: .system)        
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(loggedIn), for: .touchUpInside)
        return button
    }()

Now the following code show's how I added UIScrollView to my ViewController, then UIView as a subview to UIScrollView and then UIButton as a subview to UIView.

        view.addSubview(scrollView)
        scrollView.addSubview(contentView)

        let safeAreaGuide = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: safeAreaGuide.leadingAnchor),
            scrollView.widthAnchor.constraint(equalTo: safeAreaGuide.widthAnchor),
            scrollView.trailingAnchor.constraint(equalTo: safeAreaGuide.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: safeAreaGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: safeAreaGuide.bottomAnchor)
        ])
        
        NSLayoutConstraint.activate([
            contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
        ])

        contentView.addSubview(logInButton)

        NSLayoutConstraint.activate([
            logInButton.topAnchor.constraint(equalTo: logInInputContainer.bottomAnchor, constant: 16),
            logInButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            logInButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            logInButton.heightAnchor.constraint(equalToConstant: 50)
        ])

Somehow UIButton is placed correctly on the screen yet can not be pressed. As a workaround I've tried adding UIButton directly to UIScrollView and it actually fixes the problem. It made me think that adding contentView.isUserInteractionEnabled = true would be a solution for the original task. It didn't help though.

What's the root of the issue and how to make objects placed in a UIView which is placed in UIScrollView interactable?


Solution

  • when I run your code you are taking the wrong constraints, I just edit your constraints you can replace your code with the following and test if it works for you, in this piece of code, I just add the background colour to the button just to check the button's appearance.

    private lazy var logInButton: UIButton = {
           let button = UIButton(type: .system)
           button.translatesAutoresizingMaskIntoConstraints = false
           button.backgroundColor = .systemRed
           button.addTarget(self, action: #selector(loggedIn), for: 
           .touchUpInside)
           return button
       }()
    

    and the other piece of code that I change is

        view.addSubview(scrollView)
        scrollView.addSubview(contentView)
        contentView.addSubview(logInButton)
        
        let safeAreaGuide = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
                scrollView.leadingAnchor.constraint(equalTo: 
                safeAreaGuide.leadingAnchor),
                scrollView.widthAnchor.constraint(equalTo: 
                safeAreaGuide.widthAnchor),
                scrollView.trailingAnchor.constraint(equalTo: 
                safeAreaGuide.trailingAnchor),
                scrollView.topAnchor.constraint(equalTo: 
                safeAreaGuide.topAnchor),
                scrollView.bottomAnchor.constraint(equalTo: 
                safeAreaGuide.bottomAnchor)
        ])
        NSLayoutConstraint.activate([
                contentView.leadingAnchor.constraint(equalTo: 
                 scrollView.leadingAnchor),
                contentView.widthAnchor.constraint(equalTo: 
                scrollView.widthAnchor),
                contentView.trailingAnchor.constraint(equalTo: 
                 scrollView.trailingAnchor),
                contentView.topAnchor.constraint(equalTo: 
                 scrollView.topAnchor),
                contentView.bottomAnchor.constraint(equalTo: 
                scrollView.bottomAnchor),
        ])
        NSLayoutConstraint.activate([
                logInButton.topAnchor.constraint(equalTo: 
                 contentView.topAnchor, constant: 16),
                logInButton.leadingAnchor.constraint(equalTo: 
                 contentView.leadingAnchor, constant: 16),
                logInButton.trailingAnchor.constraint(equalTo: 
                 contentView.trailingAnchor, constant: -16),
                logInButton.bottomAnchor.constraint(equalTo: 
                 contentView.bottomAnchor, constant: -16),
                logInButton.heightAnchor.constraint(equalToConstant: 50),
        ])
    

    and the reason you were unable to tap on the button is you are taking the wrong constraints and the button is going outside the content view so you are unable to tap. now the final output will look like:

      [ Please open the below link and check the ScreenShot of Simulator ][1]
    
      [1]: https://i.sstatic.net/9otvC.png