How do I have to modify my UIView extension if I wanted to animate the auto layout constraints afterward?

六月ゝ 毕业季﹏ 提交于 2021-01-28 18:11:07

问题


I have made an UIView extension to set my NSLayout anchors. Everything works just fine. But how can I modify my extension if I wanted to add an NSLayoutConstraint so I could animate the constraints afterward?

Here is my current extension:

extension UIView {
    func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero) {
        //translate the view's autoresizing mask into Auto Layout constraints
        translatesAutoresizingMaskIntoConstraints = false

        if let top = top {
            topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
        }

        if let leading = leading {
            leadingAnchor.constraint(equalTo: leading, constant: padding.left).isActive = true
        }

        if let bottom = bottom {
            bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom).isActive = true
        }

        if let trailing = trailing {
            trailingAnchor.constraint(equalTo: trailing, constant: -padding.right).isActive = true
        }

        if size.width != 0 {
            widthAnchor.constraint(equalToConstant: size.width).isActive = true
        }

        if size.height != 0 {
            heightAnchor.constraint(equalToConstant: size.height).isActive = true
        }
    }
}

And here is how the extension is called:

//feedViewButton constraints
feedViewButton.anchor(top: nil, leading: nil, bottom: view.safeAreaLayoutGuide.bottomAnchor, trailing: view.trailingAnchor, padding: .init(top: 0, left: 0, bottom: 0, right: 25), size: .init(width: 50, height: 50))

UPDATE

I would like to have something like this

var topAnchor: NSLayoutConstraint?

topAnchor = topAnchor.constraint(equaltTo: top, constant: padding.top)
topAnchor.isActive = true

And then animate it like this:

let animator = UIViewPropertyAnimator(duration: 1, curve: .easeOut) {
    topAnchor.constant = 20
}
animator.startAnimation()

回答1:


Just try to animate it using autolayout, since the constraints haven't been applied yet, it should be animatable:

feedViewButton.anchor(top: nil, leading: nil, bottom: view.safeAreaLayoutGuide.bottomAnchor, trailing: view.trailingAnchor, padding: .init(top: 0, left: 0, bottom: 0, right: 25), size: .init(width: 50, height: 50))
feedViewButton.superview?.setNeedsLayout()
UIView.animate(withDuration: 0.2, delay: 0, options: [.allowUserInteraction], animations: {
    feedViewButton.superview?.layoutIfNeeded()
}, completion: nil)

UPDATE

If you want to later change the constants on the anchors to animate some other change, you have to keep references to the constraints to be able to manipulate them later:

enum ConstraintType {
    case top, leading, trailing, bottom, width, height
}

extension UIView {

    func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero) -> [ConstraintType : NSLayoutConstraint] {
        //translate the view's autoresizing mask into Auto Layout constraints
        translatesAutoresizingMaskIntoConstraints = false

        var constraints: [ConstraintType : NSLayoutConstraint] = [:]

        if let top = top {
            constraints[.top] = topAnchor.constraint(equalTo: top, constant: padding.top)
        }

        if let leading = leading {
            constraints[.leading] = leadingAnchor.constraint(equalTo: leading, constant: padding.left)
        }

        if let bottom = bottom {
            constraints[.bottom] = bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom)
        }

        if let trailing = trailing {
            constraints[.trailing] = trailingAnchor.constraint(equalTo: trailing, constant: -padding.right)
        }

        if size.width != 0 {
            constraints[.width] = widthAnchor.constraint(equalToConstant: size.width)
        }

        if size.height != 0 {
            constraints[.height] = heightAnchor.constraint(equalToConstant: size.height)
        }
        let constraintsArray = Array<NSLayoutConstraint>(constraints.values)
        NSLayoutConstraint.activate(constraintsArray)
        return constraints
    }
}

This extension returns a dictionary of constraints that you can later change and animate those changes. E.g.:

let constraints = feedViewButton.anchor(top: nil, leading: nil, bottom: view.safeAreaLayoutGuide.bottomAnchor, trailing: view.trailingAnchor, padding: .init(top: 0, left: 0, bottom: 0, right: 25), size: .init(width: 50, height: 50))
// applies the constraints    
self.view.layoutIfNeeded()

// now to animate bottom to -50:
if let bottomConstraint = constraints[.bottom] {
    bottomConstraint.constant = -50
    self.view.setNeedsLayout()
    let animator = UIViewPropertyAnimator(duration: 1, curve: .easeInOut, animations: {
        self.view.layoutIfNeeded()
    })
    animator.startAnimation()
}



回答2:


You can simply add the animation on the extension, so all calls to anchor will be animated without add the animation block always.

extension UIView {

    func anchor(animated: Bool, top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero) {
        //translate the view's autoresizing mask into Auto Layout constraints
        translatesAutoresizingMaskIntoConstraints = false

        if let top = top {
            topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
        }

        if let leading = leading {
            leadingAnchor.constraint(equalTo: leading, constant: padding.left).isActive = true
        }

        if let bottom = bottom {
            bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom).isActive = true
        }

        if let trailing = trailing {
            trailingAnchor.constraint(equalTo: trailing, constant: -padding.right).isActive = true
        }

        if size.width != 0 {
            widthAnchor.constraint(equalToConstant: size.width).isActive = true
        }

        if size.height != 0 {
            heightAnchor.constraint(equalToConstant: size.height).isActive = true
        }
        if animated {
            UIView.animate(withDuration: 0.2, delay: 0, options: [.allowUserInteraction], animations: {
                self.layoutIfNeeded()
            }, completion: nil)
        } else {
            self.layoutIfNeeded()
        }
    }
}


来源:https://stackoverflow.com/questions/48804229/how-do-i-have-to-modify-my-uiview-extension-if-i-wanted-to-animate-the-auto-layo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!