Swift - 以编程方式刷新约束

Posted

技术标签:

【中文标题】Swift - 以编程方式刷新约束【英文标题】:Swift - Programmatically refresh constraints 【发布时间】:2020-03-04 12:44:46 【问题描述】:

我的 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 并返回到具有更改底部约束的那个时,它会检测到 tabBar 并将 stackView 提升到另一个视图(未附加约束)下。就像是再次刷新了约束。我正在隐藏并显示这个带有开/关屏幕约束的 stackView。

tabBar.isHidden = false后需要刷新约束,但是约束没有检测到tabBar的出现。

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

【问题讨论】:

你必须创建一个底部对齐的出口并在显示标签栏时更改其常量值。 不要这个方法。我想刷新约束。 您的tabBar 是否已添加到您的视图中?或者,您的 VC 是隐藏标签栏的UITabBarController 的标签之一吗?或者,您的 VC 是否嵌入在导航控制器中,并且您将底部栏称为 tabBar UITabBarController 是 MAIN Controller,tabBar 的索引 0 上是 stackView 【参考方案1】:

这种业余方法修复了我的错误...: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()


【讨论】:

这可能是一个 hack,但实际上效果很好!【参考方案2】:

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

【讨论】:

【参考方案3】:

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

stackView.superview?.layoutIfNeeded()

【讨论】:

他没有更新约束......他只是希望他的堆栈随着标签栏兄弟的显示和隐藏而调整 不用担心,我会花掉我的一些声誉来获得赏金,一些专业人士会回答这个问题:D【参考方案4】:

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

快速搜索提供了各种UITabBarController 扩展来显示/隐藏标签栏...但它们似乎都将标签栏向下推到屏幕外,而不是设置其.isHidden 属性。可能适合也可能不适合您使用。

我从您的 cmets 中假设您的选项卡索引 0 中的 VC 有一个按钮(或其他操作)来显示/隐藏 tabBar?

如果是这样,这是一种可能完成工作的方法....

在你的项目中添加这个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 的 .isHidden 属性后简单地将 .setNeedsLayout().layoutIfNeeded() 配对应该可以完成这项工作,但显然不是。

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

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

【讨论】:

我不能更改视图的框架

以上是关于Swift - 以编程方式刷新约束的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Swift 以编程方式添加约束

以编程方式设置约束的问题 Swift

StackView 中的按钮约束(以 Swift 编程方式)

以编程方式使用约束 Swift 移动按钮

以编程方式向 Swift 中的视图添加约束时出错

Swift - UIButton 以编程方式设置约束