约束不会被停用
Constraint doesn't get deactivated
我正在以编程方式练习自动布局。我想把一个 UIView 放在控制器的中心,它的宽度在纵向模式下是 4/5,但是当它进入横向模式时,我需要高度是超级视图高度的 4/5,而不是宽度.
类似 -
所以,我根据方向停用然后激活所需的约束,但是当我改变旋转时,它给我冲突,就好像它没有停用那些,我指定要停用。这是我的完整代码。由于它独立于故事板,因此只需将视图控制器 class 分配给视图控制器即可查看效果。
class MyViewController: UIViewController {
var widthSizeClass = UIUserInterfaceSizeClass.unspecified
var centeredView : UIView = {
let view = UIView()
view.backgroundColor = UIColor.systemGreen
return view
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.addSubview(centeredView)
centeredView.translatesAutoresizingMaskIntoConstraints = false
}
override func viewWillLayoutSubviews(){
super.viewWillLayoutSubviews()
widthSizeClass = self.traitCollection.horizontalSizeClass
addConstrainsToCenterView()
}
func addConstrainsToCenterView() {
centeredView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
centeredView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
let compactWidthAnchor = centeredView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 4/5)
let compactHeightAnchor = centeredView.heightAnchor.constraint(equalTo: centeredView.widthAnchor)
let regularHeightAnchor = centeredView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 4/5)
let regularWidthAnchor = centeredView.widthAnchor.constraint(equalTo: centeredView.heightAnchor)
if widthSizeClass == .compact{
NSLayoutConstraint.deactivate([regularWidthAnchor, regularHeightAnchor])
NSLayoutConstraint.activate([compactWidthAnchor, compactHeightAnchor])
}
else{
NSLayoutConstraint.deactivate([compactWidthAnchor, compactHeightAnchor])
NSLayoutConstraint.activate([regularWidthAnchor, regularHeightAnchor])
}
}
}
谁能帮我检测一下我的缺陷。
可能不是答案,但为了配合我的评论,这是我成功使用的代码:
var p = [NSLayoutConstraint]()
var l = [NSLayoutConstraint]()
注意,p
和l
是数组,分别代表纵向和横向。
override func viewDidLoad() {
super.viewDidLoad()
setupConstraints()
}
这里没什么,只是说明加载视图时可以设置约束。
func setupConstraints() {
// for constraints that do not change, set `isActive = true`
// for constants that do change, use `p.append` and `l.append`
// for instance:
btnLibrary.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
p.append(btnLibrary.topAnchor.constraint(equalTo: safeAreaView.topAnchor, constant: 10))
l.append(btnLibrary.bottomAnchor.constraint(equalTo: btnCamera.topAnchor, constant: -10))
同样,这里没有什么 - 看起来你正在这样做。这是我在您的视图控制器覆盖中看到的不同之处:
var initialOrientation = true
var isInPortrait = false
override func viewWillLayoutSubviews() {
super.viewDidLayoutSubviews()
if initialOrientation {
initialOrientation = false
if view.frame.width > view.frame.height {
isInPortrait = false
} else {
isInPortrait = true
}
view.setOrientation(p, l)
} else {
if view.orientationHasChanged(&isInPortrait) {
view.setOrientation(p, l)
}
}
}
public func orientationHasChanged(_ isInPortrait:inout Bool) -> Bool {
if self.frame.width > self.frame.height {
if isInPortrait {
isInPortrait = false
return true
}
} else {
if !isInPortrait {
isInPortrait = true
return true
}
}
return false
}
public func setOrientation(_ p:[NSLayoutConstraint], _ l:[NSLayoutConstraint]) {
NSLayoutConstraint.deactivate(l)
NSLayoutConstraint.deactivate(p)
if self.bounds.width > self.bounds.height {
NSLayoutConstraint.activate(l)
} else {
NSLayoutConstraint.activate(p)
}
}
其中一些内容对您的使用来说可能有些过分。但是我实际上检查了边界,而不是大小 classes,同时检测了初始方向。给我用?我实际上是在设置侧边栏或底部栏。适用于所有 iPhone 和 iPad。
同样,我没有看到任何重要的东西 - activating/deactivating 一个命名的(?)约束数组而不是创建数组,这样做的顺序,你正在使用的覆盖......那个跳出来的东西(对我来说)是看大小 classes。 (可能会找出初始大小 class 是多少?)
我目前正在记录 UISplitViewController
如何决定显示次要或紧凑 VC。事实证明,它至少在 五个 组中表现不同 - iPad(始终是次要),iPad 分屏(在所有 iPad 中都是紧凑的,除了iPad Pro 12.9 在横屏时半屏),iPhone 竖屏(总是紧凑),最后,iPhone 横屏(对于大多数来说紧凑,但对于 (iPhone 8 Plus , iPhone 11, iPhone 11 Pro Max, 和 iPhone 12 Pro Max.)
注意:iPhone 11 Pro、iPhone 12 和 iPhone 12 Pro 非常紧凑!我对此感到惊讶。 (我的下一步是直接测试尺寸 classes。)
我的观点?也许您需要在屏幕边界处确定您想要的布局而不是大小 classes。这比大小 classes 更受您的控制。不管怎样,祝你好运!
很简单。每次布局发生,每次你说例如:
let regularWidthAnchor = centeredView.widthAnchor.constraint(equalTo: centeredView.heightAnchor)
NSLayoutConstraint.deactivate([regularWidthAnchor, regularHeightAnchor])
您没有停用界面中现有的活动约束。您正在创建一个全新的约束,然后将其停用(这毫无意义,因为它从未激活过),然后将其丢弃。
您只需创建一次这些约束并保留对它们的引用。
夫妻问题...
1 - 许多 iPhone 型号 只有 有 wC hR
(纵向)和 wC hC
(横向)尺寸 classes .因此,如果您在这些设备上检查 .horizontalSizeClass
,它将始终是 .compact
。您可能想要检查 .verticalSizeClass
2 - 您拥有代码的方式,每次调用 addConstrainsToCenterView()
时都会创建 NEW 约束。您没有激活/停用 现有约束。
看看这个:
class MyViewController: UIViewController {
var heightSizeClass = UIUserInterfaceSizeClass.unspecified
var centeredView : UIView = {
let view = UIView()
view.backgroundColor = UIColor.systemGreen
return view
}()
// constraints to activate/deactivate
var compactAnchor: NSLayoutConstraint!
var regularAnchor: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(centeredView)
centeredView.translatesAutoresizingMaskIntoConstraints = false
// centeredView is Always centerX and centerY
centeredView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
centeredView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
// for a square (1:1 ratio) view, it doesn't matter whether we set
// height == width
// or
// width == height
// so we can set this Active all the time
centeredView.heightAnchor.constraint(equalTo: centeredView.widthAnchor).isActive = true
// create constraints to activate / deactivate
// for regular height, set the width to 4/5ths the width of the view
regularAnchor = centeredView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 4/5)
// for compact height, set the height to 4/5ths the height of the view
compactAnchor = centeredView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 4/5)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// use .verticalSizeClass
heightSizeClass = self.traitCollection.verticalSizeClass
updateCenterViewConstraints()
}
func updateCenterViewConstraints() {
if heightSizeClass == .compact {
// if height is compact
regularAnchor.isActive = false
compactAnchor.isActive = true
}
else{
// height is regular
compactAnchor.isActive = false
regularAnchor.isActive = true
}
}
}
通过这种方法,我们为我们想要的约束创建了两个变量 activate/deactivate:
// constraints to activate/deactivate
var compactAnchor: NSLayoutConstraint!
var regularAnchor: NSLayoutConstraint!
然后,在 viewDidLoad()
中,我们将 centeredView
添加到视图,设置其“不变”约束 - centerX、centerY、aspect-ratio - 并创建两个 activate/deactivate 约束。
当我们改变大小class时,我们只需要处理两个var
约束。
我正在以编程方式练习自动布局。我想把一个 UIView 放在控制器的中心,它的宽度在纵向模式下是 4/5,但是当它进入横向模式时,我需要高度是超级视图高度的 4/5,而不是宽度.
类似 -
所以,我根据方向停用然后激活所需的约束,但是当我改变旋转时,它给我冲突,就好像它没有停用那些,我指定要停用。这是我的完整代码。由于它独立于故事板,因此只需将视图控制器 class 分配给视图控制器即可查看效果。
class MyViewController: UIViewController {
var widthSizeClass = UIUserInterfaceSizeClass.unspecified
var centeredView : UIView = {
let view = UIView()
view.backgroundColor = UIColor.systemGreen
return view
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.addSubview(centeredView)
centeredView.translatesAutoresizingMaskIntoConstraints = false
}
override func viewWillLayoutSubviews(){
super.viewWillLayoutSubviews()
widthSizeClass = self.traitCollection.horizontalSizeClass
addConstrainsToCenterView()
}
func addConstrainsToCenterView() {
centeredView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
centeredView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
let compactWidthAnchor = centeredView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 4/5)
let compactHeightAnchor = centeredView.heightAnchor.constraint(equalTo: centeredView.widthAnchor)
let regularHeightAnchor = centeredView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 4/5)
let regularWidthAnchor = centeredView.widthAnchor.constraint(equalTo: centeredView.heightAnchor)
if widthSizeClass == .compact{
NSLayoutConstraint.deactivate([regularWidthAnchor, regularHeightAnchor])
NSLayoutConstraint.activate([compactWidthAnchor, compactHeightAnchor])
}
else{
NSLayoutConstraint.deactivate([compactWidthAnchor, compactHeightAnchor])
NSLayoutConstraint.activate([regularWidthAnchor, regularHeightAnchor])
}
}
}
谁能帮我检测一下我的缺陷。
可能不是答案,但为了配合我的评论,这是我成功使用的代码:
var p = [NSLayoutConstraint]()
var l = [NSLayoutConstraint]()
注意,p
和l
是数组,分别代表纵向和横向。
override func viewDidLoad() {
super.viewDidLoad()
setupConstraints()
}
这里没什么,只是说明加载视图时可以设置约束。
func setupConstraints() {
// for constraints that do not change, set `isActive = true`
// for constants that do change, use `p.append` and `l.append`
// for instance:
btnLibrary.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
p.append(btnLibrary.topAnchor.constraint(equalTo: safeAreaView.topAnchor, constant: 10))
l.append(btnLibrary.bottomAnchor.constraint(equalTo: btnCamera.topAnchor, constant: -10))
同样,这里没有什么 - 看起来你正在这样做。这是我在您的视图控制器覆盖中看到的不同之处:
var initialOrientation = true
var isInPortrait = false
override func viewWillLayoutSubviews() {
super.viewDidLayoutSubviews()
if initialOrientation {
initialOrientation = false
if view.frame.width > view.frame.height {
isInPortrait = false
} else {
isInPortrait = true
}
view.setOrientation(p, l)
} else {
if view.orientationHasChanged(&isInPortrait) {
view.setOrientation(p, l)
}
}
}
public func orientationHasChanged(_ isInPortrait:inout Bool) -> Bool {
if self.frame.width > self.frame.height {
if isInPortrait {
isInPortrait = false
return true
}
} else {
if !isInPortrait {
isInPortrait = true
return true
}
}
return false
}
public func setOrientation(_ p:[NSLayoutConstraint], _ l:[NSLayoutConstraint]) {
NSLayoutConstraint.deactivate(l)
NSLayoutConstraint.deactivate(p)
if self.bounds.width > self.bounds.height {
NSLayoutConstraint.activate(l)
} else {
NSLayoutConstraint.activate(p)
}
}
其中一些内容对您的使用来说可能有些过分。但是我实际上检查了边界,而不是大小 classes,同时检测了初始方向。给我用?我实际上是在设置侧边栏或底部栏。适用于所有 iPhone 和 iPad。
同样,我没有看到任何重要的东西 - activating/deactivating 一个命名的(?)约束数组而不是创建数组,这样做的顺序,你正在使用的覆盖......那个跳出来的东西(对我来说)是看大小 classes。 (可能会找出初始大小 class 是多少?)
我目前正在记录 UISplitViewController
如何决定显示次要或紧凑 VC。事实证明,它至少在 五个 组中表现不同 - iPad(始终是次要),iPad 分屏(在所有 iPad 中都是紧凑的,除了iPad Pro 12.9 在横屏时半屏),iPhone 竖屏(总是紧凑),最后,iPhone 横屏(对于大多数来说紧凑,但对于 (iPhone 8 Plus , iPhone 11, iPhone 11 Pro Max, 和 iPhone 12 Pro Max.)
注意:iPhone 11 Pro、iPhone 12 和 iPhone 12 Pro 非常紧凑!我对此感到惊讶。 (我的下一步是直接测试尺寸 classes。)
我的观点?也许您需要在屏幕边界处确定您想要的布局而不是大小 classes。这比大小 classes 更受您的控制。不管怎样,祝你好运!
很简单。每次布局发生,每次你说例如:
let regularWidthAnchor = centeredView.widthAnchor.constraint(equalTo: centeredView.heightAnchor)
NSLayoutConstraint.deactivate([regularWidthAnchor, regularHeightAnchor])
您没有停用界面中现有的活动约束。您正在创建一个全新的约束,然后将其停用(这毫无意义,因为它从未激活过),然后将其丢弃。
您只需创建一次这些约束并保留对它们的引用。
夫妻问题...
1 - 许多 iPhone 型号 只有 有 wC hR
(纵向)和 wC hC
(横向)尺寸 classes .因此,如果您在这些设备上检查 .horizontalSizeClass
,它将始终是 .compact
。您可能想要检查 .verticalSizeClass
2 - 您拥有代码的方式,每次调用 addConstrainsToCenterView()
时都会创建 NEW 约束。您没有激活/停用 现有约束。
看看这个:
class MyViewController: UIViewController {
var heightSizeClass = UIUserInterfaceSizeClass.unspecified
var centeredView : UIView = {
let view = UIView()
view.backgroundColor = UIColor.systemGreen
return view
}()
// constraints to activate/deactivate
var compactAnchor: NSLayoutConstraint!
var regularAnchor: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(centeredView)
centeredView.translatesAutoresizingMaskIntoConstraints = false
// centeredView is Always centerX and centerY
centeredView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
centeredView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
// for a square (1:1 ratio) view, it doesn't matter whether we set
// height == width
// or
// width == height
// so we can set this Active all the time
centeredView.heightAnchor.constraint(equalTo: centeredView.widthAnchor).isActive = true
// create constraints to activate / deactivate
// for regular height, set the width to 4/5ths the width of the view
regularAnchor = centeredView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 4/5)
// for compact height, set the height to 4/5ths the height of the view
compactAnchor = centeredView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 4/5)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// use .verticalSizeClass
heightSizeClass = self.traitCollection.verticalSizeClass
updateCenterViewConstraints()
}
func updateCenterViewConstraints() {
if heightSizeClass == .compact {
// if height is compact
regularAnchor.isActive = false
compactAnchor.isActive = true
}
else{
// height is regular
compactAnchor.isActive = false
regularAnchor.isActive = true
}
}
}
通过这种方法,我们为我们想要的约束创建了两个变量 activate/deactivate:
// constraints to activate/deactivate
var compactAnchor: NSLayoutConstraint!
var regularAnchor: NSLayoutConstraint!
然后,在 viewDidLoad()
中,我们将 centeredView
添加到视图,设置其“不变”约束 - centerX、centerY、aspect-ratio - 并创建两个 activate/deactivate 约束。
当我们改变大小class时,我们只需要处理两个var
约束。