UIScrollview 动画从视图到视图的过渡

UIScrollview Animation Transition From View To View

我试图在用户滚动分页视图时执行过渡动画,即:从 第 1 页第 2 页 .

不幸的是,我无法复制它。

下面附上我做的:

class OnboardingParallaxImageView: BaseUIView, UIScrollViewDelegate {

    let allImages = [#imageLiteral(resourceName: "onboarding_handshake_icon"), #imageLiteral(resourceName: "onboarding_paylinc_icon")]

    var activeCurrentPage = 1

    let bgView: UIImageView = {
        let image = #imageLiteral(resourceName: "onboard_bg_gradient")
        let view = UIImageView(image: image)
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    let firstImageView: UIImageView = {
        let view = UIImageView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    let secondImageView: UIImageView = {
        let view = UIImageView()
        view.isHidden = true
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    var firstImageHeightAnchor: NSLayoutConstraint?
    var firstImageWidthAnchor: NSLayoutConstraint?
    var secondImageHeightAnchor: NSLayoutConstraint?
    var secondImageWidthAnchor: NSLayoutConstraint?

    override func setupViews() {
        super.setupViews()
        addSubview(bgView)
        addSubview(firstImageView)
        addSubview(secondImageView)

        bgView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
        bgView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
        bgView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        bgView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true

        firstImageWidthAnchor = firstImageView.widthAnchor.constraint(equalTo: widthAnchor)
        firstImageWidthAnchor?.isActive = true
        firstImageHeightAnchor = firstImageView.heightAnchor.constraint(equalTo: heightAnchor)
        firstImageHeightAnchor?.isActive = true
        firstImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        firstImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        firstImageView.image = allImages[0]

        secondImageWidthAnchor = secondImageView.widthAnchor.constraint(equalTo: widthAnchor)
        secondImageWidthAnchor?.isActive = true
        secondImageHeightAnchor = secondImageView.heightAnchor.constraint(equalTo: heightAnchor)
        secondImageHeightAnchor?.isActive = true
        secondImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        secondImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        secondImageView.image = allImages[1]
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let frameWidth = frame.size.width
        secondImageHeightAnchor?.constant = -(frameWidth - 32)
        secondImageWidthAnchor?.constant = -(frameWidth - 32)
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let offSet = scrollView.contentOffset.x

        let frameWidth = frame.size.width / 2

        let toUseConstant = (CGFloat(abs(offSet)) / frameWidth)

        if activeCurrentPage == 1 {
            if offSet <= 0 {
                firstImageHeightAnchor?.constant = 0
                firstImageWidthAnchor?.constant = 0
                firstImageView.isHidden = false

                secondImageView.isHidden = true
            } else {
                firstImageHeightAnchor?.constant += -(toUseConstant)
                firstImageWidthAnchor?.constant += -(toUseConstant)
                firstImageView.isHidden = false

                secondImageHeightAnchor?.constant += -(toUseConstant)
                secondImageWidthAnchor?.constant += -(toUseConstant)
                secondImageView.isHidden = false
                secondImageView.alpha = toUseConstant
            }
        }

        UIView.animate(withDuration: 0.5) {
            self.layoutSubviews()
        }
    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        self.activeCurrentPage = scrollView.currentPage
    }
}

这是我能够实现的结果:

我怎样才能从 A 过渡到 B 而没有任何有趣的行为。

谢谢

您可能要考虑为此使用框架,而不是重新发明轮子。例如 Hero

有一些基本的 examples 可以帮助您入门。从本质上讲,这为您提供了一个易于使用的选项,通过定义每个 UI-Component 应该如何根据过渡进度进行行为来编码两个 UIViewController 之间的过渡行为。

我在ImageView的帧上使用关键帧动画实现了它。但是您也可以在尝试时使用基于约束的动画来实现它。只需根据您的方便用约束常量的更改替换我在这里所做的帧大小变化。

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var imgView1: UIImageView! //HANDSHAKE image on TOP
    @IBOutlet weak var imgView2: UIImageView! //CREDIT_CARD and PHONE image
    @IBOutlet weak var imgView3: UIImageView! //SINGLE_HAND image


    let minimumFrameSize = CGSize(width: 0.0, height: 0.0)
    let meetingPointFrameSize = CGSize(width: 150.0, height: 150.0)
    let maximumFrameSize  = CGSize(width: 400.0, height: 400.0)


    override func viewDidLoad() {
        super.viewDidLoad()

        let centerOfImages = CGPoint(x: self.view.frame.width/2 , y: self.view.frame.height/2)

        //initial state
        self.imgView1.frame.size = maximumFrameSize
        self.imgView1.alpha = 1.0

        self.imgView2.frame.size = minimumFrameSize
        self.imgView2.alpha = 0.0

        self.imgView3.frame.size = minimumFrameSize
        self.imgView3.alpha = 0.0

        self.imgView1.center = centerOfImages
        self.imgView2.center = centerOfImages
        self.imgView3.center = centerOfImages

        UIView.animateKeyframes(withDuration: 5.0, delay: 2.0, options: [UIView.KeyframeAnimationOptions.repeat,
            UIView.KeyframeAnimationOptions.calculationModeLinear],
                                animations: {

            UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations: {
                self.imgView1.frame.size = self.meetingPointFrameSize
                self.imgView1.alpha = 0.3
                self.imgView1.center = centerOfImages

                self.imgView2.frame.size = self.meetingPointFrameSize
                self.imgView2.alpha = 0.3
                self.imgView2.center = centerOfImages

                /*image 1 and image 2 meets at certain point where
                  image 1 is decreasing its size and image 2 is increasing its size simultaneously
                 */

            })

            UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25, animations: {
                self.imgView1.frame.size = self.minimumFrameSize
                self.imgView1.alpha = 0.0
                self.imgView1.center = centerOfImages

                self.imgView2.frame.size = self.maximumFrameSize
                self.imgView2.alpha = 1.0
                self.imgView2.center = centerOfImages

                /* image 1 has decreased its size to zero and
                   image 2 has increased its size to maximum simultaneously
                 */

            })



                      UIView.addKeyframe(withRelativeStartTime: 0.4, relativeDuration: 0.1, animations: {
                            self.imgView2.frame.size = self.maximumFrameSize
                            self.imgView2.alpha = 1.0
                            self.imgView2.center = centerOfImages
                                        /* Hold for a moment
                                         */
                        })



            UIView.addKeyframe(withRelativeStartTime: 0.6, relativeDuration: 0.15, animations: {
                self.imgView2.frame.size = self.meetingPointFrameSize
                self.imgView2.alpha = 0.3
                self.imgView2.center = centerOfImages

                self.imgView3.frame.size = self.meetingPointFrameSize
                self.imgView3.alpha = 0.3
                self.imgView3.center = centerOfImages

                /*image 2 and image 3 meets at certain point where
                 image 2 is decreasing its size and image 3 is increasing its size simultaneously
                 */
            })


            UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25, animations: {
                self.imgView2.frame.size = self.minimumFrameSize
                self.imgView2.alpha = 0.0
                self.imgView2.center = centerOfImages

                self.imgView3.frame.size = self.maximumFrameSize
                self.imgView3.alpha = 1.0
                self.imgView3.center = centerOfImages

                /* image 2 has decreased its size to zero and
                   image 3 has increased its size to maximum simultaneously
                 */
            })


        }) { (finished) in
            /*If you have any doubt or need more enhancement or in case you need my project. Feel free to ask me.
             Please ping me on skype/email:
                ojhashubham29@gmail.com
             or connect me on Twitter
                @hearthackman
             */
        }
    }


}

实现流畅行为的最简单方法是使用 UIViewPropertyAnimator。 为每个图像视图设置初始值:

override func viewDidLoad() {
        super.viewDidLoad()

        firstImageView.image = // your image

        secondImageView.image = // your image
        secondImageView.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
        secondImageView.alpha = 0
    }

然后为每个 imageView

创建 属性 animator
lazy var firstAnimator: UIViewPropertyAnimator = {

// You can play around with the duration, curve, damping ration, timing
        let animator = UIViewPropertyAnimator(duration: 2, curve: .easeIn, animations: {
            self.firstImageView.image.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
            self.firstImageView.image.alpha = 0
        })

        return animator
    }()

和第二个

lazy var secondAnimator: UIViewPropertyAnimator = {

        let animator = UIViewPropertyAnimator(duration: 2, curve: .easeIn, animations: {
            self.secondImageView.transform = CGAffineTransform(scaleX: 1, y: 1)
            self.secondImageView.alpha = 1
        })

        return animator
    }()

现在 scrollViewDidScroll 当您计算完成百分比时,只需更新动画师:

firstAnimator.fractionComplete = calculatedPosition
secondAnimator.fractionComplete = calculatedPosition

您可以对多个视图应用相同的方法