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()
}
我的 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()
}