【问题标题】:Auto Layout views with animation带有动画的自动布局视图
【发布时间】:2018-05-08 19:38:58
【问题描述】:

我有一个自定义视图,CustomLayout(蓝色,自定义 UIView),该视图包含 3 个使用约束(布局锚)垂直对齐的子视图,每个视图都按照以下顺序对齐:

  • 1 个视图:SlideLayout(红色,自定义 UIView)
  • 2 视图:UIButton(黄色)
  • 3 视图:UIView(干灰色)

我希望当我点击按钮(黄色)时,SlideLayout(红色)的高度大小在打开时增加或在关闭时通过动画减小。如果SlideLayout增加/减少,其他视图必须在动画期间改变位置,并且父视图(CustomLayout)必须增加/减少他的高度大小(动画)。

我使用这个方法的时候调用了什么方法:

UIView.animate(withDuration、延迟、选项、动画、完成)

我通过添加一个简单的打印来覆盖 layoutIfNeeded() 方法,但它不会在动画期间每次都调用

我尝试了这个,但它没有像我预期的那样工作。我该如何解决这个问题,谢谢。

Bad animation in picture

代码:

class CustomLayout: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    private func setup() {
        onLayout()
    }

    public func onLayout() {
        print("\(frame.size.height)")
        let MARGIN: CGFloat = 10
        for i in 0 ..< subviews.count {
            let child = subviews[i]
                if i == 0 { // slide layout
                    child.translatesAutoresizingMaskIntoConstraints = false
                    child.topAnchor.constraint(equalTo: topAnchor, constant: MARGIN).isActive = true
                    child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
                    child.widthAnchor.constraint(equalToConstant: frame.size.width - (MARGIN * 2)).isActive = true
                    let enchorHeight = child.heightAnchor.constraint(equalToConstant: child.frame.size.height);
                    enchorHeight.isActive = true
                    (subviews[0] as! SlideDownLayout).enchorHeight = enchorHeight
                }
                else if i == 1 { // button
                    child.translatesAutoresizingMaskIntoConstraints = false
                    child.topAnchor.constraint(equalTo: subviews[0].bottomAnchor, constant: MARGIN).isActive = true
                    child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
                    child.widthAnchor.constraint(equalToConstant: child.frame.size.width).isActive = true
                    child.heightAnchor.constraint(equalToConstant: child.frame.size.height).isActive = true

                }
                else if i == 2 { // uiview  
                    child.translatesAutoresizingMaskIntoConstraints = false    
                    child.topAnchor.constraint(equalTo: subviews[1].bottomAnchor, constant: MARGIN).isActive = true                
                    child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true                
                    child.widthAnchor.constraint(equalToConstant: frame.size.width - (MARGIN * 4)).isActive = true 
                    child.heightAnchor.constraint(equalToConstant: 300).isActive = true               
                    bottomAnchor.constraint(equalTo: child.bottomAnchor, constant: MARGIN).isActive = true

                }        
        }    
    }
}

class SlideDownLayout: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    private var HEIGHT: CGFloat = 0
    private var isClosed: Bool = true

    private func setup() {
        HEIGHT = frame.height
        frame.size.height = 0
    }

    public func slideAnimation(view: UIView) {
        print("\(HEIGHT)")
        isClosed = !isClosed
        self.enchorHeight!.constant = self.isClosed ? 0 : self.HEIGHT
         UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut, animations: {
            self.superview?.layoutIfNeeded()
            view.layoutIfNeeded()
         }, completion: nil)
    }

    override func layoutIfNeeded() {
        print("...")
        super.layoutIfNeeded()
    }

    var enchorHeight: NSLayoutConstraint? = nil
}

class ViewController: UIViewController {

    @IBOutlet weak var customLayout: CustomLayout!
    @IBOutlet weak var slideDownLayout: SlideDownLayout!

    override func viewDidLoad() {
        super.viewDidLoad()

        customLayout.translatesAutoresizingMaskIntoConstraints = false
        customLayout.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        customLayout.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        customLayout.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
        customLayout.topAnchor.constraint(equalTo: view.topAnchor).isActive = true

    }

    override var prefersStatusBarHidden: Bool {
        return true
    }

    @IBAction
    func buttonListener(_ sender: Any) {
        slideDownLayout.slideAnimation(view: self.view)
    }
}

【问题讨论】:

  • 用stackView试试这个

标签: ios swift animation autolayout


【解决方案1】:

当您想要制作动态视图时,不要更改框架,而是更改约束的常量,因为框架不会将父级向下推,因此将要动画的视图的高度约束设置为 IBOutlet 并控制它的恒定值

你可以试试

class CustomLayout: UIView {

   var heightCon:NSLayoutConstraint!

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    private func setup() {
        onLayout()
    }

    public func onLayout() {
        print("\(frame.size.height)")
        let MARGIN: CGFloat = 10
        for i in 0 ..< subviews.count {
            let child = subviews[i]
                if i == 0 { // slide layout
                    child.translatesAutoresizingMaskIntoConstraints = false
                    child.topAnchor.constraint(equalTo: topAnchor, constant: MARGIN).isActive = true
                    child.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
                    child.widthAnchor.constraint(equalToConstant: frame.size.width - (MARGIN * 2)).isActive = true
                   heightCon =  child.heightAnchor.constraint(equalToConstant: child.frame.size.height) 
                   heightCon.isActive = true 
                }

//

    public func slideAnimation() {
        print("\(HEIGHT)")

         isClosed = !isClosed

         let ss = self.superview as! CustomLayout

         ss.heightCon.constant = self.isClosed ? 0 : self.HEIGHT

         UIView.animate(withDuration: 1, delay: 0, options: .curveEaseInOut, animations: { 
            ss.layoutIfNeeded()
         }, completion: nil)
    }

【讨论】:

  • 谢谢你,我没有看到你的代码更新我按照你说的做了并且动画工作但是父尺寸没有增加或减少,我更新了我的代码
  • 钩住最底部视图的底部约束
  • 我更新了我的代码,我有底部约束,但视图在打开时不会从顶部开始,并且底部边距超过父视图
  • 在 i = 2 处设置视图高度
  • 感谢您的帮助我将这一行 "child.bottomAnchor.constraint(equalTo: bottomAnchor, constant: MARGIN).isActive = true" 替换为 "bottomAnchor.constraint(equalTo: child.bottomAnchor, constant : MARGIN).isActive = true" (child i = 2 CustmLayout) 现在它工作正常
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
  • 2013-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多