iOS swift: change imageview image in the middle of

2019-08-17 03:27发布

I have an opening book animation: https://streamable.com/n1c0n

And I want to on 90 degrees my image has changed.

I use this code:

var book1ImageViewI : UIImageView
let book2ImageViewI : UIImageView

book1ImageViewI = UIImageView(frame: CGRect( x: self.view.frame.size.width / 2 - 140, y: (self.view.frame.size.height / 2) - ( (self.view.frame.width / 2) / 2 ), width: ( (self.view.frame.width / 2) / 8) * 7, height: self.view.frame.width / 2))

book2ImageViewI = UIImageView(frame: CGRect(x: self.view.frame.size.width / 2 - 140, y: (self.view.frame.size.height / 2) - ( (self.view.frame.width / 2) / 2 ), width: ( (self.view.frame.width / 2) / 8) * 7, height: self.view.frame.width / 2))


book1ImageViewI.image = UIImage(named:"attachment_83090027.jpg")
book2ImageViewI.image = UIImage(named:"0a6752b7cd35fc441c152238ee5078384d--antique-books-rabbit-hole.jpg")

book1ImageViewI.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
book2ImageViewI.layer.anchorPoint = CGPoint(x: 0, y: 0.5)

var transform = CATransform3DIdentity
transform.m34 = 1.0 / -2000.0
book1ImageViewI.layer.transform = transform

UIView.animate(withDuration: 1.5, animations: {
    book1ImageViewI.layer.transform = CATransform3DRotate(transform, -.pi/2, 0, 1, 0)
})
{
    (bCompleted) in
    if (bCompleted) {
        book1ImageViewI.layer.transform = CATransform3DRotate(transform, -.pi/2, 0, 1, 0)
    }

    UIView.animate(withDuration: 1.5, animations: {
        book1ImageViewI.layer.transform = CATransform3DRotate(transform, .pi*0.999, 0, 1, 0)
        book1ImageViewI.image = UIImage(named:"0a6752b7cd35fc441c1528ee5078384d--antique-books-rabbit-holer.png")
        }, completion: {
            (bFinished) in
            //Whatever
    })
}

self.view.addSubview(book2ImageViewI)
self.view.addSubview(book1ImageViewI)

Everything works fine. But on 90 degrees animation slightly delayed.

I want to get animation like this without delay: https://streamable.com/q2bf6

How to do it?

P.S. In second animation use this code:

UIView.animate(withDuration: 3,  delay: 0.0,
                       options: [], animations: {

    DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
        book1ImageViewI.image = UIImage(named:"0a6752b7cd35fc441c1528ee5078384d--antique-books-rabbit-holer.png")
    }

    book1ImageViewI.layer.transform = CATransform3DRotate(transform, .pi, 0, 1, 0)

    self.view.layoutIfNeeded()

    }, completion: {_ in

})

But this code does not fit. Because images changes not on 90 degrees. Sometimes earlier. Sometimes later.

2条回答
狗以群分
2楼-- · 2019-08-17 03:40

This part of your code conflicts:

(bCompleted) in
    if (bCompleted) {
        book1ImageViewI.layer.transform = CATransform3DRotate(transform, -.pi/2, 0, 1, 0)
    }

    UIView.animate(withDuration: 1.5, animations: {
        book1ImageViewI.layer.transform = CATransform3DRotate(transform, .pi*0.999, 0, 1, 0)
        book1ImageViewI.image = UIImage(named:"0a6752b7cd35fc441c1528ee5078384d--antique-books-rabbit-holer.png")
        }, completion: {
            (bFinished) in
            //Whatever
    })

You set book1ImageViewI.layer.transform = CATransform3DRotate(transform, -.pi/2, 0, 1, 0) at the same time as book1ImageViewI.layer.transform = CATransform3DRotate(transform, .pi*0.999, 0, 1, 0)

Try this instead:

(bCompleted) in
    if (bCompleted) {

         UIView.animate(withDuration: 1.5, animations: {
             book1ImageViewI.layer.transform = CATransform3DRotate(transform, -.pi/2, 0, 1, 0)
             book1ImageViewI.image = UIImage(named:"0a6752b7cd35fc441c1528ee5078384d--antique-books-rabbit-holer.png")
             }, completion: {
                 (bFinished) in
                 //Whatever
         })
    }
查看更多
我只想做你的唯一
3楼-- · 2019-08-17 03:45

Apparently, the anchor-point change was quite crucial wherever rotation transform is involved, and it is not straightforward. Every time you change anchorpoint, it alters the UIView position, and we must compensate for this change somehow.

Here is the final version using imageview which works without interruption for me. Apparently, it works with addKeyFrame API as well but for simplicity's sake I kept UIView.animate().

The trick I used is to have smaller duration for first animation and larger one for the second. It almost look like a page flip. You can also play with UIView.animate overrides that provide for UIViewAnimationOptions - such as curveEaseIn and curveEaseOut to make it look like page flip of a book.

It seems to work for me, the only thing is that you can't say it is rotating from the front or back. If your imageview is slant (like the slant book page you show in video), it should be visible and you can simply correct by changing the - to + sign within CATransform3DRotate arguments, or vice versa in animation block.

NOTE:

If it still plays hide and seek, try on real device instead of simulator just in case. There is apparent difference due to GPU execution when it comes to animations.

func pageFlipAnimation()
{
    self.myImageView.image = UIImage.init(named: "firstImage")

    var transform = CATransform3DIdentity
    let transform1 = CATransform3DRotate(transform, -CGFloat(Double.pi)*0.5, 0, 1, 0)
    let transform2 = CATransform3DRotate(transform, -CGFloat(Double.pi), 0, 1, 0)

    transform.m34 = 1.0 / -5000.0

    self.setAnchorPoint(anchorPoint: CGPoint(x: 0, y: 0.5), forView: self.myImageView)

    UIView.animate(withDuration: 0.5, animations:
    {
        self.myImageView.layer.transform = transform1
    })
    {
        (bFinished) in
        self.myImageView.image = UIImage.init(named: "secondImage")

        UIView.animate(withDuration: 0.75, animations:
        {
             self.myImageView.layer.transform = transform2
        })
    }
}

//This should ideally be a UIView Extension
func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView)
{
    var newPoint = CGPoint(x:view.bounds.size.width * anchorPoint.x, y:view.bounds.size.height * anchorPoint.y)
    var oldPoint = CGPoint(x:view.bounds.size.width * view.layer.anchorPoint.x, y:view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = newPoint.applying(view.transform)
    oldPoint = oldPoint.applying(view.transform)

    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}
查看更多
登录 后发表回答