Auto layout Animation

Thursday, March 8, 2018

基于 Autolayout 下的动画处理

  • 现在使用 autolayout 创建 UI 的情况越来越多,在情况下,执行动画需要进行一些特别处理。而不是直接对 frame 进行修改。

  • 先看效果图 [WPGP gif_id="102” width="400”]

  • 上图中的所有动画,都是基于 autolayout 创建的 view 执行的。分别是『+』button,『Packing List』的 label,barView的高度变化动画,还有加载图片列表的 scrollview,点击图标的弹出框。

  • 首先讲讲最简单 barView 的高度变化,我们只需修改 lauout 的 constant 的数值,再在动画块内部实现layoutIfNeed 方法即可,代码如下:

    menuHeightConstraint.constant = isMenuOpen ? 200.0 : 60.0 titleLabel.text = isMenuOpen ? “Select Item” : “Packing List”

        UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity:10, options: .curveEaseIn, animations: {
            //在动画里执行 layoutIfNeed,才能实现动画效果
            self.view.layoutIfNeeded()
            let angle: CGFloat = self.isMenuOpen ? .pi / 4 : 0.0
            self.buttonMenu.transform = CGAffineTransform.init(rotationAngle: angle)
    
        }, completion: nil)
    
  • 上述代码还包含了『+』button 的旋转,这个只需改变 transform 的角度即可,不再详述。

  • 再到 Packing List 这个 Label,会发现该 label 约束变化较大,从最开始的居中于barView,到整体偏向左上角,这里需要修改 label 的 centerX 和 centerY,方可实现,代码如下:

    //遍历 menu bar view 的 约束,然后再进行调整 titleLabel.superview?.constraints.forEach({ (constraint) in if constraint.firstItem === titleLabel && constraint.firstAttribute == .centerX { constraint.constant = isMenuOpen ? -100.0 : 0.0 return }

            //通过 identifier 的方式找到特定的约束
            if constraint.identifier == "TitleCenterY" {
                //释放旧的约束,创建新的约束
                constraint.isActive = false
    
                let newConstraint = NSLayoutConstraint.init(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: titleLabel.superview!, attribute: .centerY, multiplier: isMenuOpen ? 0.67 : 1.0, constant: 5.0)
                newConstraint.identifier = "TitleCenterY"
                newConstraint.isActive = true
            }
    
        })
    
  • 上述代码,首先遍历的 titleLabel 的 superView 的所有 constraints,并通过两种方式分别找到 titleLabel 的 centerX 和 centerY 的约束,一种是通过找 constraint 的 firstItem 和 firstAttribute是否是 titleLabel 本身 和 centerX,另一种是通过查找 constraint 的 identifier 来找出 titleLabel的centerY 的约束,该 identifier 需要在创建约束的时候就声明好。

  • 由于修改约束只能修改 constant 不能修改 multiplier,所以改变 CenterY 的时候,需要通过释放旧的约束,并且创建新的centerY约束来实现。通过 isActive 属性来释放。

  • 最后来看看点击图标之后的弹出动画,这里我们创建了一个 View,并且使用代码的方式完成约束的创建,代码如下:

    func showItem(_ index: Int) { print(“tapped item (index)") let imageView = UIImageView.init(image: UIImage.init(named: “summericons_100px_0(index).png”)) imageView.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.5) imageView.layer.cornerRadius = 5.0 imageView.layer.masksToBounds = true imageView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(imageView)

        //创建约束
        let conX = imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let conBottom = imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: imageView.frame.height)
        let conWidth = imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.33, constant: -50.0)
        let conHeight = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor)
    
        //批量加入创建的约束
        NSLayoutConstraint.activate([conX, conBottom, conWidth, conHeight])
        //在执行动画前先执行layoutIfNeeded
        view.layoutIfNeeded()
    
        UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
            conBottom.constant = -imageView.frame.size.height/2
            conWidth.constant = 0.0
            self.view.layoutIfNeeded()
        }, completion: nil)
    
    }
    
  • 这里可以看到,创建了好几个约束后,通过 NSLayoutConstraint.activate 方法批量加入约束。特别要注意的是,在修改约束之前,需要先执行一遍 layoutIfNeed,否则效果就不是代码想要的呈现的效果了。

iOS动画

Layer Animation(一)

View Animation