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 - 以编程方式刷新约束的主要内容,如果未能解决你的问题,请参考以下文章