Search code examples
swiftuiviewcontrolleruikituitabbarcontroller

UITabBarController issue - something (UINavigationBarContentView?) overlays the button on child UIViewController so the button can't be pressed


I implemented UITabBarController with a tab made of UIViewController. It consists of two parts - UIView with UIButton placed into, and a UITableView. I didn't used UITableViewController because it's not possible to add custom buttons to its title:

final class ContactsController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@objc func buttonTapped(sender: UIButton) {
        print("Button tapped!")
    }

lazy var addButton = { [weak self] in
    guard let self = self else { return UIButton()}
    let button = UIButton(type: .custom)
    button.backgroundColor = .systemBlue
    button.tintColor = .white
    button.layer.cornerRadius = 8
    button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    return button
}()

lazy var buttonBarView = {
    let barView = UIView()
    barView.clipsToBounds = true
    // barView.isUserInteractionEnabled = true
    return barView
}()

lazy var tableView = {
    let tableView = UITableView()
    return tableView
}()
    
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 50 }

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(ContactCell.self)
    
    addButton.setTitle("Add", for: .normal)
    tableView.dataSource = self
    tableView.delegate = self

    view.backgroundColor = .systemBackground
    title = "Contacts"
    tableView.rowHeight = 125
    
    self.view.bringSubviewToFront(addButton)

    setUpViews()
}

private func setUpViews() {
    
    self.view.addSubview(buttonBarView)
    buttonBarView.addConstraints(
        .alignTopWithTop(of: self.view),
            .alignLeadingWithLeading(of: view),
            .alignWidth(with: view, mult: 1),
        .alignHeight(with: view, mult: 0.045)
        )
    
    buttonBarView.addSubview(addButton)
    addButton.addConstraints(
               .alignTopWithTop(of: buttonBarView),
               .alignBottomWithBottom(of: buttonBarView),
               .alignTrailingWithTrailing(of: buttonBarView, const: 8),
               .setAspectWidth(ratio: 1))
    
    
    self.view.addSubview(tableView)
    tableView.addConstraints(
        .alignTopWithBottom(of: buttonBarView),
        .alignLeadingWithLeading(of: view),
        .alignWidth(with: view, mult: 1),
        .alignBottomWithBottom(of: view)
    )
    

Please ignore lazy var combined with setUpViews function - this is done because I spent some hours trying to debug it.

All looks fine, but there's an issue - the button "..." can not be pressed:

button can not be pressed

If I changed constraints and moved it below "Contacts" title, it can be pressed.

I used debug views tool and noticed that may be UINavigationBarContentView overlays the button (here it is selected):

my views hierarchy

I tried to access it via code, but it isn't accessible.

Also, I used view.bringSubviewToFront() to bring the button to front, but it changed nothing.

I printed all subviews of that UIViewController and for each subview, I did the same. But print only showed me only those views that I created.

Please help resolving this issue. I checked Apple documentation on UIViewController but, unfortunately, didn't find anything useful.

.toolBarItems property is used together with UINavigationController so I think it can't be used in my case.

Thank you.


Solution

  • The problem is your use of buttonBarView. You set its constraints to be at the very top of the view controller's view. But the top of that view is behind the nav bar. And that's exactly what you see in the view hierarchy debug view.

    The simplest solution is to get rid of your buttonBarView. UINavigationBar already provides ways to add buttons (and views) to the nav bar. This is done via UIViewController APIs - navigationItem.

    You don't even need your addButton as a UIButton. Create a standard UIBarButtonItem with a standard +.

    let btnAdd = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addTapped))
    navigationItem.rightBarButtonItem = btnAdd
    

    There are ways to create the UIBarButtonItem with a title or image if you don't want the standard add icon. If you really want to use your custom button then you can create the UIBarButtonItem with the button as a custom view:

    let btnAdd = UIBarButtonItem(customView: addButton)
    navigationItem.rightBarButtonItem = btnAdd
    

    No need for the extra views or the constraints.