5

My VC starts with stackView attached with Align Bottom to Safe Area .

I have tabBar, but in the beginning is hidden tabBar.isHidden = true.

Later when the tabBar appears, it hides the stackView

So I need function that refresh constraints after tabBar.isHidden = false


When I start the app with tabBar.isHidden = false the stackView is shown properly.


Tried with every function like: stackView.needsUpdateConstraints() , updateConstraints() , setNeedsUpdateConstraints() without success.


Now I'm changing the bottom programatically, but when I switch the tabBarIndex and return to that one with changed bottom constraints it detects the tabBar and lifts the stackView under another view (which is not attached with constraints). Like is refreshing again the constraints. I'm hiding and showing this stackView with constrains on/off screen.

I need to refresh constraints after tabBar.isHidden = false, but the constraints don't detect the appearance of the tabBar.

As I mention switching between tabBars fixes the issue, so some code executes to detecting tabBar after the switch. Is anyone know this code? I tried with calling the methods viewDidLayoutSubviews and viewWillLayoutSubviews without success... Any suggestions?

4
  • You will have to create an outlet of bottom align and change its constant value when you show tabbar. Commented Mar 4, 2020 at 12:56
  • Don't want this method. I want to refresh the constraints. Commented Mar 4, 2020 at 18:02
  • Is your tabBar an object added to your view? Or, is your VC one of the tabs of a UITabBarController with the tab bar hidden? Or, is your VC embedded in a navigation controller and you refer to the bottom bar as a tabBar? Commented Mar 5, 2020 at 14:10
  • UITabBarController is MAIN Controller and on index 0 of the tabBar is the stackView Commented Mar 5, 2020 at 21:28

4 Answers 4

3

This amateur approach fixed my bug... :D

tabBarController!.selectedIndex = 1
tabBarController!.selectedIndex = 0

Or with an extension

extension UITabBarController {

    // Basically just toggles the tabs to fix layout issues
    func forceConstraintRefresh() {
    
        // Get the indices we need
        let prevIndex = selectedIndex
        var newIndex = 0
    
        // Find an unused index
        let items = viewControllers ?? []
        find: for i in 0..<items.count {
            if (i != prevIndex) {
                newIndex = i
                break find
            }
        }
    
        // Toggle the tabs
        selectedIndex = newIndex
        selectedIndex = prevIndex
    
    }

}

Usage (called when switching dark / light mode):

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    
    tabBarController?.forceConstraintRefresh()

}
Sign up to request clarification or add additional context in comments.

1 Comment

This might be a hack, but it works really well actually!
0

If you want to update view's layout, you can try layoutIfNeeded() function.

Comments

0

after updating stackView constraints call this method:

stackView.superview?.layoutIfNeeded()

2 Comments

he is not updating constraints ... he just want his stack to adjust with show and hide of tabbar bro
No worries I will spend some of my reputation for bounty and some PRO will answer the question :D
-1

Apple's Human Interface Guidelines indicate that one should not mess around with the Tab Bar, which is why (I'm guessing) setting tabBar.isHidden doesn't properly update the rest of the view hierarchy.

Quick searching comes up with various UITabBarController extensions for showing / hiding the tab bar... but they all appear to push the tabBar down off-screen, rather than setting its .isHidden property. May or may not be suitable for your use.

I'm assuming from your comments that your VC in tab index 0 has a button (or some other action) to show / hide the tabBar?

If so, here is an approach that may do the job....

Add this enum in your project:

enum TabBarState {
    case toggle, show, hide
}

and put this func in that view controller:

func showOrHideTabBar(state: TabBarState? = .toggle) {

    if let tbc = self.tabBarController {
        let b: Bool = (state == .toggle) ? !tbc.tabBar.isHidden : state == .hide
        guard b != tbc.tabBar.isHidden else {
            return
        }
        tbc.tabBar.isHidden = b
        view.frame.size.height -= 0.1
        view.setNeedsLayout()
        view.frame.size.height += 0.1
    }
}

You can call it with:

// default: toggles isHidden
showOrHideTabBar()

// toggles isHidden
showOrHideTabBar(state: .toggle)

// SHOW tabBar (if it's hidden)
showOrHideTabBar(state: .show)

// HIDE tabBar (if it's showing)
showOrHideTabBar(state: .hide)

I would expect that simply pairing .setNeedsLayout() with .layoutIfNeeded() after setting the tabBar's .isHidden property should do the job, but apparently not.

The quick frame height change (combined with .setNeedsLayout()) does trigger auto-layout, though, and the height change is not visible.

NOTE: This is the result of very brief testing, on one device and one iOS version. I expect it will work across devices and versions, but I have not done complete testing.

1 Comment

It's not an option for me to change the frame of the view

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.