iosswiftanimationtouchpage-curl

pageCurl animation interactive with touches


I'm looking for a way to indicate a page curl animation on an UIView that interactive with touch(Began/Moved/Ended) like this

Note:

  1. I can't use UIPageController. The animation should be applied to the UIView.
  2. The interaction between the animation and the start of the touch, touch movement and touch end is very important. Exactly like video

Sample Code but dont work with touch:

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.

    BackBtn.layer.cornerRadius = 5.0

    AnimationBtn.layer.cornerRadius = 5.0

    transition.delegate = self
    transition.duration = 1.5
    transition.startProgress = 0
    transition.endProgress = 1
    transition.type = CATransitionType(string: "pageCurl") as String
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    transition.fillMode = kCAFillModeBoth
    transition.isRemovedOnCompletion = false
}

in touch moved:

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    super.touchesMoved(touches, with: event)

    if let touch = touches.first {

        let endPosition = touch.location(in: touch.view)

        differenceX = startPosition.x - endPosition.x

        differenceY = endPosition.y - startPosition.y

        transition.subtype = kCATransitionFromRight

        touch.view!.layer.add(transition, forKey: kCATransition)

        webView.scrollView.contentOffset = CGPoint(x: nextPage + differenceX, y: 0)
   }
}

Solution

  • I found answer. I just need to change the transition.startProgress and transition.endProgress.

    Sample code:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
    
        calculatePageCount()
    
        if let touch = touches.first {
    
            startPosition = touch.location(in: self.view)
    
            endPosition = startPosition
    
            previousPositionX = startPosition.x
    
            print("start x: \(startPosition.x), start y: \(startPosition.y)")
    
            transition.startProgress = 0.0
    
            if index == 0 && startPosition.x > (screenWidth / 2) {
    
                transition.endProgress = 1.0 - Float(startPosition.x / screenWidth)
                transition.subtype = kCATransitionFromRight
    
                self.view.layer.add(transition, forKey: kCATransition)
            } else if index != 0 {
                if index != (pageCounter - 1) && startPosition.x > (screenWidth / 2) {
                    if self.view.layer.animationKeys() != nil {
                        self.view.layer.removeAllAnimations()
                    }
    
                    transition.endProgress = 1.0 - Float(startPosition.x / screenWidth)
                    transition.subtype = kCATransitionFromRight
                    self.view.layer.add(transition, forKey: kCATransition)
                } else if startPosition.x < (screenWidth / 2) {
                    if self.view.layer.animationKeys() != nil {
                        self.view.layer.removeAllAnimations()
                    }
    
                    transition.endProgress = Float(startPosition.x / screenWidth)
                    transition.subtype = kCATransitionFromLeft
                    self.view.layer.add(transition, forKey: kCATransition)
                } else {
                    if self.view.layer.animationKeys() != nil {
                        self.view.layer.removeAllAnimations()
                    }
                }
            } else {
                if self.view.layer.animationKeys() != nil {
                    self.view.layer.removeAllAnimations()
                }
            }
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
    
        if let touch = touches.first {
    
            endPosition = touch.location(in: touch.view)
    
            print("end x: \(endPosition.x), end y: \(endPosition.y)")
    
            differenceX = startPosition.x - endPosition.x
    
            differenceY = endPosition.y - startPosition.y
    
            let differencePosition = endPosition.x - previousPositionX
    
            previousPositionX = endPosition.x
    
            print("difference x: \(differenceX)")
    
            if self.view.layer.animationKeys() != nil {
    
                transition.startProgress = transition.endProgress
    
                if differenceX > 0 {
    
                    differencePosition < 0 ? (transition.endProgress = transition.endProgress + Float((abs(differencePosition) / screenWidth))) : (transition.endProgress = transition.endProgress - Float((abs(differencePosition) / screenWidth)))
    
                    self.view.layer.removeAllAnimations()
                    self.view.layer.add(transition, forKey: kCATransition)
                } else {
    
                    differencePosition > 0 ? (transition.endProgress = transition.endProgress + Float((abs(differencePosition) / screenWidth))) : (transition.endProgress = transition.endProgress - Float((abs(differencePosition) / screenWidth)))
    
                    self.view.layer.removeAllAnimations()
                    self.view.layer.add(transition, forKey: kCATransition)
                }
            }
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
    
        print("touch ended")
    
        if self.view.layer.animationKeys() != nil {
    
            if startPosition.x == endPosition.x {
    
                transition.startProgress = transition.endProgress
                transition.endProgress = 0
    
                self.view.layer.removeAllAnimations()
                self.view.layer.add(transition, forKey: kCATransition)
            } else {
    
                if differenceX > 0 {
    
                    if differenceX > (screenWidth / 2) {
    
                        if index < (pageCounter - 1) {
                            index += 1
                        }
                        transition.startProgress = transition.endProgress
                        transition.endProgress = 1
    
                        self.view.layer.removeAllAnimations()
                        self.view.layer.add(transition, forKey: kCATransition)
                    } else {
                        transition.endProgress = 0
    
                        self.view.layer.removeAllAnimations()
                        self.view.layer.add(transition, forKey: kCATransition)
                    }
                } else {
    
                    if abs(differenceX) > (screenWidth / 2) {
                        if index > 0 {
                            index -= 1
    
                            transition.startProgress = transition.endProgress
                            transition.endProgress = 1
    
                            self.view.layer.removeAllAnimations()
                            self.view.layer.add(transition, forKey: kCATransition)
                        } else {
                            transition.endProgress = 0
    
                            self.view.layer.removeAllAnimations()
                            self.view.layer.add(transition, forKey: kCATransition)
                        }
                    } else {
    
                        transition.endProgress = 0
    
                        self.view.layer.removeAllAnimations()
                        self.view.layer.add(transition, forKey: kCATransition)
                    }
                }
            }
        }
        nextPage = CGFloat(index) * webView.scrollView.bounds.size.width
        webView.scrollView.contentOffset = CGPoint(x: nextPage, y: 0)
    }
    
    func calculatePageCount() {
    
        pageCounter = Int((webView.scrollView.contentSize.width / webView.scrollView.bounds.size.width).rounded())
    }