我在UIViewController中添加了一个子视图。这个子视图(称为PlayerView)应该有两种状态。一个是折叠状态,其高度仅为56.它的展开状态高度为385.当子视图从折叠状态更改为展开状态时,我希望PlayerView中的子视图改变形状和位置。
当PlayerView被添加到UIViewController(MasterViewController)时,它使用layoutSubviews(),如下所示:
override func layoutSubviews() {
super.layoutSubviews()
backgroundColor = UIColor(red: 24/255, green: 24/255, blue: 24/255, alpha: 1)
roundCorners([.topLeft, .topRight], radius: 10)
addSubview(albumImage)
addSubview(songTitleLabel)
addSubview(songArtistLabel)
addSubview(playPauseButton)
_ = albumImage.anchor(nil, left: leftAnchor, bottom: nil, right: nil, topConstant: 0, leftConstant: 20, bottomConstant: 0, rightConstant: 0, widthConstant: 40, heightConstant: 40)
albumImage.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
_ = playPauseButton.anchor(nil, left: nil, bottom: nil, right: rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 30, widthConstant: 22, heightConstant: 22)
_ = songTitleLabel.anchor(albumImage.topAnchor, left: albumImage.rightAnchor, bottom: nil, right: playPauseButton.leftAnchor, topConstant: 0, leftConstant: 8, bottomConstant: 0, rightConstant: 8, widthConstant: songTitleLabel.frame.width, heightConstant: songTitleLabel.frame.height)
_ = songArtistLabel.anchor(songTitleLabel.bottomAnchor, left: songTitleLabel.leftAnchor, bottom: nil, right: playPauseButton.leftAnchor, topConstant: 2, leftConstant: 8, bottomConstant: 0, rightConstant: 8, widthConstant: songArtistLabel.frame.width, heightConstant: songArtistLabel.frame.height)
playPauseButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
按下折叠视图时,应该展开。这是我去做的方式(这是在我的MasterViewController中):
let pv = PlayerView()
func expandPlayerView() {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
self.pv.frame = CGRect(x: 0, y: CGFloat((self.view.frame.maxY) - 352), width: 375, height: 350)
self.pv.changePlayerView()
self.view.layoutSubviews()
}) { (true) in
}
}
changePlayerView()
函数的定义如下:
func changePlayerView() {
subviews.forEach({$0.removeFromSuperview()})
addSubview(playPauseButton)
playPauseButton.removeConstraints(playPauseButton.constraints)
_ = playPauseButton.anchor(nil, left: nil, bottom: bottomAnchor, right: nil, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 22, heightConstant: 22)
playPauseButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
}
执行所有代码后会发生什么:
1. playerView扩展到385罚款
2.所有其他子视图都将从playerView中删除
3.播放暂停按钮几乎占据整个PlayerView
子视图,并且不符合其宽度和高度约束22
我需要做些什么才能使playPauseButton符合其所有约束条件?
编辑:
.anchor
函数在UIView的扩展中定义,如下所示:
func anchor(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
translatesAutoresizingMaskIntoConstraints = false
var anchors = [NSLayoutConstraint]()
if let top = top{
anchors.append(topAnchor.constraint(equalTo: top, constant: topConstant))
}
if let left = left{
anchors.append(leftAnchor.constraint(equalTo: left, constant: leftConstant))
}
if let bottom = bottom{
anchors.append(bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant))
}
if let right = right{
anchors.append(rightAnchor.constraint(equalTo: right, constant: -rightConstant))
}
if widthConstant > 0{
anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
}
if heightConstant > 0{
anchors.append(heightAnchor.constraint(equalToConstant: heightConstant))
}
anchors.forEach({$0.isActive = true})
return anchors
}
答案 0 :(得分:0)
我无法说出您的代码中发生了什么,但您试图以与我不同的方式更改约束。这是我为改变约束而做的事情:
声明所有一致的或全局约束 - 那些不会像往常一样改变"设置isActive = true
的约束。无需重写东西。
根据您的需要,明确设置一次更改或一次更改的约束。
永远不要使用removeConstraints
! (虽然它可能更像是个人规则,但我从来没有找到使用它的好用例。)而是设置isActive
或使用NSLayoutConstraint.deactivate
和NSLayoutConstraint.activate
对于你的阵列。
除非您有特殊需要,否则不删除子视图!与视图层次结构混淆可能会导致约束缺失或不完整。相反,请使用isHidden = true
。
我认为这是我的最后一个要点,这是你的结果的原因。
以下是设置两个约束数组的示例,一个用于纵向,一个用于横向。假设我有两个观点,我希望在画像中彼此相对,但在风景中彼此相邻。
在我的视图控制器中,我有两个视图:
var p = [NSLayoutConstraint]()
var l = [NSLayoutConstraint]()
var initialOrientation = true
var isInPortrait = false
var greenView = UIView()
var redView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
view.translatesAutoresizingMaskIntoConstraints = false
greenView.translatesAutoresizingMaskIntoConstraints = false
redView.translatesAutoresizingMaskIntoConstraints = false
greenView.backgroundColor = UIColor.green
redView.backgroundColor = UIColor.red
view.addSubview(greenView)
view.addSubview(redView)
setupConstraints()
}
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)
}
}
}
我创建了两个扩展。这是视图控制器扩展:
extension ViewController {
func setupConstraints() {
// create generic layout guides
let layoutGuideTop = UILayoutGuide()
view.addLayoutGuide(layoutGuideTop)
let layoutGuideBottom = UILayoutGuide()
view.addLayoutGuide(layoutGuideBottom)
let margins = view.layoutMarginsGuide
view.addLayoutGuide(margins)
if #available(iOS 11, *) {
let guide = view.safeAreaLayoutGuide
layoutGuideTop.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0).isActive = true
layoutGuideBottom.bottomAnchor.constraintEqualToSystemSpacingBelow(guide.bottomAnchor, multiplier: 1.0).isActive = true
} else {
layoutGuideTop.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
layoutGuideBottom.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
}
p.append(greenView.topAnchor.constraint(equalTo: layoutGuideTop.topAnchor))
p.append(greenView.leadingAnchor.constraint(equalTo: margins.leadingAnchor))
p.append(greenView.trailingAnchor.constraint(equalTo: margins.trailingAnchor))
p.append(greenView.heightAnchor.constraint(equalTo: redView.heightAnchor))
p.append(redView.topAnchor.constraint(equalTo: greenView.bottomAnchor))
p.append(redView.leadingAnchor.constraint(equalTo: margins.leadingAnchor))
p.append(redView.trailingAnchor.constraint(equalTo: margins.trailingAnchor))
p.append(redView.bottomAnchor.constraint(equalTo: layoutGuideBottom.bottomAnchor))
l.append(greenView.topAnchor.constraint(equalTo: layoutGuideTop.topAnchor))
l.append(greenView.leadingAnchor.constraint(equalTo: margins.leadingAnchor))
l.append(greenView.widthAnchor.constraint(equalTo: redView.widthAnchor))
l.append(greenView.bottomAnchor.constraint(equalTo: layoutGuideBottom.bottomAnchor))
l.append(redView.topAnchor.constraint(equalTo: layoutGuideTop.topAnchor))
l.append(redView.leadingAnchor.constraint(equalTo: greenView.trailingAnchor))
l.append(redView.bottomAnchor.constraint(equalTo: layoutGuideBottom.bottomAnchor))
l.append(redView.trailingAnchor.constraint(equalTo:margins.trailingAnchor))
}
}
真正的魔法在UIView
扩展名中,我激活/停用这两个数组:
extension 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)
}
}
}
我更改约束的另一种方法是直接声明某些内容并更改乘数,常量或优先级。主要技巧是记住你需要单独设置isActive
标志。 (不要问我为什么。它在Xcode中输入不当,我打赌苹果称之为"行为"。)
var myButtonWidth = myButton.widthAnchor.constraint(equalToConstant: 100)
myButtonWidth.isActive = true
改变是,只需代码反对它:
func changeMyButton() {
myButtonWidth.constant = 200
}
希望其中一些有帮助。