Swift - 以编程方式刷新约束

Swift - Programmatically refresh constraints

我的 VC 以 stackView 开头并附有 Align Bottom to Safe Area

我有tabBar,但是一开始是隐藏的tabBar.isHidden = true

稍后当 tabBar 出现时,它会隐藏 stackView

所以我需要在 tabBar.isHidden = false

之后刷新约束的函数

当我使用 tabBar.isHidden = false 启动应用程序时,stackView 会正确显示。


尝试了每个功能,例如:stackView.needsUpdateConstraints() , updateConstraints() , setNeedsUpdateConstraints() 但没有成功。


现在我正在以编程方式更改底部,但是当我将 tabBarIndex 和 return 切换到底部约束已更改的那个时,它会检测到 tabBar 并将 stackView 提升到另一个视图(未附加约束)。就像是再次刷新约束。我正在隐藏和显示带有约束 on/off 屏幕的这个 stackView。

我需要在 tabBar.isHidden = false 后刷新约束,但约束没有检测到 tabBar 的外观。

正如我提到的在 tabBar 之间切换修复了这个问题,所以一些代码会在切换后执行以检测 tabBar。有人知道这段代码吗?我尝试调用方法 viewDidLayoutSubviews 和 viewWillLayoutSubviews 但没有成功...有什么建议吗?

如果你想更新视图的布局,你可以试试layoutIfNeeded()函数。

更新 stackView 约束后调用此方法:

stackView.superview?.layoutIfNeeded()

Apple 的 Human Interface Guidelines 表示不应乱用选项卡栏,这就是为什么(我猜)设置 tabBar.isHidden 无法正确更新视图层次结构的其余部分。

快速搜索得出了各种 UITabBarController 用于显示/隐藏选项卡栏的扩展...但它们似乎都将 tabBar 向下推到屏幕外,而不是设置其 .isHidden 属性。可能适合也可能不适合您的使用。

根据您的评论,我假设您的选项卡索引 0 中的 VC 有一个按钮(或其他一些操作)来显示/隐藏选项卡栏?

如果是这样,这里有一种方法可以完成这项工作....

在您的项目中添加此 enum

enum TabBarState {
    case toggle, show, hide
}

并将此函数放入该视图控制器中:

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
    }
}

你可以这样调用它:

// 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)

我会期望在设置tabBar的[=16=之后简单地将.setNeedsLayout().layoutIfNeeded()配对] 属性 应该可以完成这项工作,但显然不是。

框架高度的快速变化(结合 .setNeedsLayout() 触发自动布局,但高度变化不可见。

注意:这是very简短测试的结果,在一台设备上一个 iOS 版本。我希望它可以跨设备和版本工作,但我还没有完成完整的测试。

这种业余方法修复了我的错误...:D

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

或使用扩展名

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
    
    }

}

用法(切换暗/亮模式时调用):

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

}