设备方向更改时更新约束

时间:2020-09-08 15:57:45

标签: swift xcode uibutton autolayout

我想根据设备方向更改按钮高度限制。我正在创建高度限制。然后我为风景模式设置了高度限制60,为人像模式设置了40。但是当我更改设备方向时,高度不会变大。问题出在哪儿。这是我的代码

lazy var nextEpisodeButton: CustomPlayerButton = {
    let nextEpisode = CustomPlayerButton(type: .nextEpisode, backgroundImage: nil)
    nextEpisode.addTarget(self, action: #selector(nextEpisodeTapped), for: .touchUpInside)
    nextEpisode.translatesAutoresizingMaskIntoConstraints = false
    nextEpisode.adjustsImageWhenHighlighted = false
    return nextEpisode
}()

func addNextEpisodeButton() {
    view.addSubview(nextEpisodeButton)
    NSLayoutConstraint.activate([
        nextEpisodeButton.heightAnchor.constraint(equalToConstant: 40),
        nextEpisodeButton.widthAnchor.constraint(equalToConstant: 120),
        nextEpisodeButton.rightAnchor.constraint(equalTo: view.safeRightAnchor, constant: -20),
        nextEpisodeButton.bottomAnchor.constraint(equalTo: view.safeBottomAnchor, constant: -60)
    ])
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.willTransition(to: size, with: coordinator)
    
    if UIDevice.current.orientation.isLandscape {
        nextEpisodeButton.heightAnchor.constraint(equalToConstant: 60).isActive = true
    } else {
        nextEpisodeButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
    }
    nextEpisodeButton.layoutIfNeeded()

}

3 个答案:

答案 0 :(得分:0)

您应该在ViewController中的某个地方引用nextEpisodeButton.heightAnchor.constraint(equalToConstant: 40)约束,而在willTransition回调中只需更改其constant值即可。使用您的代码,您可以在每次旋转时创建并激活一个新约束,而不是更改现有约束。

答案 1 :(得分:0)

约束是持久的。当您为同一属性(在您的情况下为视图的高度)创建第二个约束并激活该约束时,这两个约束相互冲突。

因此,您需要为它们两个都保留一个引用,并相应地激活/停用它们:

'demo    
Cells.Clear
bigno = 12345
For i = 1 To 20
Cells(i, 1) = bigno
If Rnd > 0.8 Then bigno = bigno + Int(Rnd * 10000) + 10000
Next i
'code
usn = 1
Cells(1, 2) = usn
For i = 2 To 20
If Cells(i, 1) <> Cells(i - 1, 1) Then usn = usn + 1
Cells(i, 2) = usn
Next i

答案 2 :(得分:0)

我的方法是做两件事:

  • 在要激活和停用的数组中保留要更改的约束。
  • 通过特征以外的其他方法检测设备方向

让我们从第一个开始。您将有一个良好的开端-约束是(a)代码中的约束和(b)使用锚点的约束。 (如果您偶然使用IB-其他的故事板,则需要将更改的约束设置为@IBOutlets。)

您似乎希望此按钮位于右下角,因此让这些约束处于活动状态:

nextEpisodeButton.rightAnchor.constraint(equalTo: view.safeRightAnchor, constant: -20).isActive = true
nextEpisodeButton.bottomAnchor.constraint(equalTo: view.safeBottomAnchor, constant: -60).isActive = true

无论方向如何,这都会正确地固定东西。

现在,假设您要更改尺寸。您需要将它们放入两个数组:

var portraitLayout = [NSLayoutConstraint]()
var landscapeLayout = [NSLayoutConstraint]()

portraitLayout.append(nextEpisodeButton.widthAnchor.constraint(equalToConstant: 120))
portraitLayout.append(nextEpisodeButton.heightAnchor.constraint(equalToConstant: 40))

landscapeLayout.append(nextEpisodeButton.widthAnchor.constraint(equalToConstant: 120))
landscapeLayout.append(nextEpisodeButton.heightAnchor.constraint(equalToConstant: 60))

这将在纵向中设置40x120按钮,在横向中设置60x120按钮。可以(应该?)在viewDidLoad中完成。现在该激活/停用了。...

一个数组应该处于活动状态,并且在初始化视图时需要执行一个操作。我会讲到的,但首先,让我显示两行必要的代码:

NSLayoutConstraint.deactivate(landscapeLayout)
NSLayoutConstraint.activate(portraitLayout)

可以尝试添加/删除约束,但这不仅冒险,而且不那么容易维护,甚至不需要。只需将所有约束设置为常数isActive = true,将要更改的约束放入数组,然后激活/停用。

(如果您想为这样的更改添加动画效果-我不希望您这样做-然后执行此操作并在末尾添加UIView.animate(withDuration:)。)

现在,粗糙的工件正在检测方向。

苹果几年前决定添加特征集。他们运作良好(今年,我终于明白了为什么他们会像以前那样做)。但是它们有一个严重的问题-全屏模式iPad总是具有正常大小。 (我今年正在编写一个仅限iPad的应用,在分屏模式下,它可能非常紧凑。)

您的问题强调方向,所以我建议使用特征收集更改。而是使用viewWillLayoutSubviews。对我来说,这似乎更可靠-这是我发现的最早的视图控制器生命周期。您需要做两件事...设置初始方向并检测更改。

这是我的设置。在UIView扩展名中:

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)
    }
}

pl分别是纵向和横向。我要做的只是简单地检查界限,并适当地启用/停用。

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)
        }
    }
}

这可能满足您的需求。我基本上是在跟踪两件事(初始方向和当前方向)以进行更改,并在需要时调用它们-viewWillLayoutSubviews在load(?)期间可以被多次调用,并且除方向改变之外还可以出于其他原因。

结论:

您很亲密,但有一些更改。首先,将常量约束设置为isActive = true,然后将其余约束作为数组激活/去激活。其次,除非您的应用程序仅是iPhone(即使那时它仍然可以在iPad上使用),否则请不要使用特征收集,而应使用视图控制器生命周期和屏幕边界。