如何在设备旋转后重新计算约束
Posted
技术标签:
【中文标题】如何在设备旋转后重新计算约束【英文标题】:How to recalculate constraints after device rotation 【发布时间】:2019-04-01 21:14:57 【问题描述】:在我的表格视图的单元格中,我有包含视图的堆栈视图。我有堆栈视图的尾随约束。我根据要在堆栈视图中显示的视图数量来计算此尾随约束的值。
我们有什么方法可以重新计算这个尾随约束值并显示视图,而无需在设备旋转后重新加载viewWillTransition
中的表视图?
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
super.viewWillTransition(to: size, with: coordinator)
self.tableView.reloadData()
// self.view.layoutIfNeeded() // This doesn't recalculate constraints
【问题讨论】:
目前发生了什么,试过self.view.layoutIfNeeded()
它不会重新计算约束值。它只能通过在旋转后重新加载表格视图来工作。
我添加了执行 self.view.layoutIfNeeded() 时发生的屏幕截图,您可以看到它不会重新计算约束。
【参考方案1】:
你应该实现这个方法
func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
当设备转换到新视图(横向或纵向)时调用。在此您可以更改约束并调用
view.setNeedsLayout()
标记该布局需要在下一个 Runloop 中重新计算。
【讨论】:
【参考方案2】:有几种方法可以做到这一点——在旋转(或改变尺寸)时重新计算尺寸可能不是最好的方法。
利用UIStackView
处理所有布局的一种方法是将“空白”视图添加到您的堆栈视图中。如果您设置仅显示 1 或 2 个“真实”视图,您还可以显示“空白”视图以填充这 3 个“列”。
因此,假设您可能会显示零实际视图的行,请保持原型不变,但是:
向堆栈视图添加 IBOutlet 引用 在awakeFromNib()
中,创建并添加3 个清除UIView
s 到堆栈视图
跟踪数组中的真实视图和空白视图
现在,当您设置要显示的视图时,隐藏要显示的视图不和显示足够的空白视图以保持 3 个视图可见。
例子:
[1, 2]
隐藏view3
并显示blank1
[2]
隐藏 view1
和 view3
并显示 blank1
和 blank2
因此,您的堆栈中将始终有 3 个子视图,并且不需要计算...自动布局将使它们保持排列。
这是一个示例实现:
class ThreeColCell: UITableViewCell
@IBOutlet var mainStackView: UIStackView!
@IBOutlet var view1: UIView!
@IBOutlet var view2: UIView!
@IBOutlet var view3: UIView!
var arrayOfRealViews: [UIView] = [UIView]()
var arrayOfBlankViews: [UIView] = [UIView]()
var myData: [Int] = [Int]()
didSet
// hide all the views in the stack
mainStackView.arrangedSubviews.forEach
$0.isHidden = true
// show the specified "real" views
myData.forEach i in
arrayOfRealViews[i - 1].isHidden = false
// if fewer than 3 "real" views, show "blank" view(s)
for i in 0..<(3 - myData.count)
arrayOfBlankViews[i].isHidden = false
override func awakeFromNib()
super.awakeFromNib()
commonInit()
func commonInit() -> Void
// ordered array of views 1 to 3
arrayOfRealViews = [view1, view2, view3]
// add 3 "blank" views to the stack view
// and to array of blank views
for _ in 0..<3
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .clear
mainStackView.addArrangedSubview(v)
arrayOfBlankViews.append(v)
class ThreeColTableViewController: UITableViewController
var theData = [
[1, 2],
[1, 2, 3],
[1],
[1, 2, 3],
[2],
[2, 3],
[1, 2, 3],
[3],
[2, 3],
[1, 2, 3],
]
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return theData.count
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "ThreeColCell", for: indexPath) as! ThreeColCell
cell.myData = theData[indexPath.row]
return cell
导致纵向:
和风景:
编辑:
这是另一个示例,使用具有 .isDisabled1
(以及 2 和 3)属性的结构数组:
struct Profile
var isDisabled1 = false
var isDisabled2 = false
var isDisabled3 = false
class ThreeColCell: UITableViewCell
@IBOutlet var mainStackView: UIStackView!
@IBOutlet var view1: UIView!
@IBOutlet var view2: UIView!
@IBOutlet var view3: UIView!
var arrayOfRealViews: [UIView] = [UIView]()
var arrayOfBlankViews: [UIView] = [UIView]()
var myProfile: Profile = Profile()
didSet
// hide all the views in the stack
mainStackView.arrangedSubviews.forEach
$0.isHidden = true
// I don't know how you have your button/label views set up, but here
// you would set button titles and label texts based on myProfile properties
// create a [1, 2, 3] array based on the .isDisabled# properties of the Profile object
var a = [Int]()
if !myProfile.isDisabled1
a.append(1)
if !myProfile.isDisabled2
a.append(2)
if !myProfile.isDisabled3
a.append(3)
// you now have an array "a" that will be
// [1, 2, 3] or
// [1, 2] or
// [2] or
// [2, 3] etc
// show the specified "real" views (arrays are Zero-based)
a.forEach i in
arrayOfRealViews[i - 1].isHidden = false
// pad stackview to 3 using "blank" view(s)
// if 1 real view, show 2 blank views
// if 2 real views, show 1 blank view
// if 3 real views, don't show any blank views
for i in 0..<(3 - a.count)
arrayOfBlankViews[i].isHidden = false
override func awakeFromNib()
super.awakeFromNib()
// ordered array of views 1 to 3
arrayOfRealViews = [view1, view2, view3]
// add 3 "blank" views to the stack view
// and to array of blank views
for _ in 0..<3
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .clear
mainStackView.addArrangedSubview(v)
arrayOfBlankViews.append(v)
class ThreeColTableViewController: UITableViewController
var profilesArray: [Profile] = [Profile]()
override func viewDidLoad()
super.viewDidLoad()
// create a few Profiles
// all 3 views are "enabled" by default
var p = Profile()
profilesArray.append(p)
// Profile with views 2 and 3 disabled
p = Profile()
p.isDisabled2 = true
p.isDisabled3 = true
profilesArray.append(p)
// Profile with view 3 disabled
p = Profile()
p.isDisabled3 = true
profilesArray.append(p)
// Profile with view 1 disabled
p = Profile()
p.isDisabled1 = true
profilesArray.append(p)
// Profile with views 1 and 2 disabled
p = Profile()
p.isDisabled1 = true
p.isDisabled2 = true
profilesArray.append(p)
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return profilesArray.count
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "ThreeColCell", for: indexPath) as! ThreeColCell
cell.myProfile = profilesArray[indexPath.row]
return cell
【讨论】:
看起来像一个解决方案,但它太复杂了,我头晕目眩。我的数据不是 int 数组,而是结构数组,它有很多字段。 “结构数组,它有很多字段” --- 没有任何区别。据推测,您的数据结构中的某些内容指示视图是否应该可见,因此使用该值来显示/隐藏视图和“空白”视图。 @Davis - 我编辑了我的答案以包含另一个示例......这一次,使用“配置文件”对象数组而不是简单的 Ints 数组。以上是关于如何在设备旋转后重新计算约束的主要内容,如果未能解决你的问题,请参考以下文章