簡體   English   中英

按下按鈕時更改錨點/約束

[英]Change anchors/Constraints when a button is pressed

我在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 fine 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
}

我無法說出代碼中發生了什么,但是您嘗試更改約束的方式與我完全不同。 這是我傾向於更改約束的方法:

  • 聲明所有一致約束或全局約束-那些不會“像往常一樣”更改的約束,設置isActive = true 無需重新編寫東西。

  • 明確設置那些可以更改的約束,可以一次變化,也可以數組變化,具體取決於您的需求。

  • 切勿使用removeConstraints (盡管這可能更多是個人規則,但我從未找到使用它的好用例。)而是為數組設置isActive或使用NSLayoutConstraint.deactivateNSLayoutConstraint.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中輸入不正確,我敢打賭Apple稱其為“行為”。)

var myButtonWidth = myButton.widthAnchor.constraint(equalToConstant: 100)
myButtonWidth.isActive = true

而要更改的是,只需對其進行編碼:

func changeMyButton() {
    myButtonWidth.constant = 200
}

希望其中一些幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM