Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> สมาร์ทโฟน >> iPhone

เชี่ยวชาญ iOS Animations ด้วย CAAnimation: ผ่านความกลัว สร้าง Motion ที่ลื่นไหล

บทความนี้เน้นการใช้ CA Animations ใน iOS เพื่อทำแอนิเมชันที่ลื่นไหล

ในช่วงแรกทำงานกับ iOS ผมจะหวั่นไหวทุกครั้งที่นักออกแบบมาหาและขอแอนิเมชันในแอปที่พวกเขาทำอยู่

เชี่ยวชาญ iOS Animations ด้วย CAAnimation: ผ่านความกลัว สร้าง Motion ที่ลื่นไหล
Craaaaap

ผมเคยคิดว่าออกแบบแอนิเมชันง่าย — แต่ Implement ฝั่งอื่น ยากมาก

ผมขอความช่วยเหลือจาก Google, StackOverflow, และเพื่อนร่วมงาน ในระหว่างกระบวนการ ผมพัฒนาภาวะฟوبيาเรื่องแอนิเมชัน และพยายามหลีกเลี่ยงมันตลอด แต่มันเปลี่ยนไปวันหนึ่ง

การค้นพบ CAAnimation

ครั้งหนึ่ง ผมต้องทำแอนิเมชัน Sequence ของ Images ใน View ขั้นแรกของผมคือ? แน่นอน StackOverflow!

ลิงก์แรกได้โค้ด

let image_1 = UIImage(named: "image-1")!
let image_2 = UIImage(named: "image-2")!
let image_3 = UIImage(named: "image-3")!
let images = [image_1, image_2, image_3]
let animatedImage = UIImage.animatedImage(with: images, duration: 2.0)
imageView.image = animatedImage
เชี่ยวชาญ iOS Animations ด้วย CAAnimation: ผ่านความกลัว สร้าง Motion ที่ลื่นไหล

ดูตรงไปตรงมาใช่ไหม? ถ้าง่ายขนาดนั้นผมไม่เขียนบทความนี้

นี่คือแอนิเมชันที่ต้องการ:

เชี่ยวชาญ iOS Animations ด้วย CAAnimation: ผ่านความกลัว สร้าง Motion ที่ลื่นไหล
End Goal

และชัดเจนว่าผมอยู่ไกลมาก ผมติดอยู่ จะทำ Customizations เยอะอย่างนั้นและ Sync ทั้งหมดได้อย่างไร?

เพื่อนร่วมงานบอกให้ลอง CAAnimation ผมอ่านและลองบน Sample Project ตกตกใจ มันทรงพลังและใช้ง่ายจริงๆ

Core Animation คืออะไร?

Core Animation ช่วย Execute Multiple Animations ด้วย CPU Usage เกือบศูนย์
ให้ High Frame Rate และ Customizations เยอะที่ใช้ได้ด้วยโค้ดน้อย

รายละเอียดเพิ่มเติมใน Docs: https://developer.apple.com/documentation/quartzcore

ผม Implement Basic ได้ในไม่กี่ชั่วโมง:

func addAnimation(firstImageView: UIImageView, secondImageView: UIImageView) {
    let basicAnimation1 = getBasicAnimation(withInitialPostion: centerPosition, finalPos: finalPosition)
    firstImageView.layer.add(basicAnimation1, forKey: "position")
    let basicAnimation2 = self.getBasicAnimation(withInitialPostion: self.initalPosition, finalPos: self.centerPosition)
    secondImageView.layer.add(basicAnimation2, forKey: "position")
    self.addNextImage(forImageView: firstImageView)
}
func getBasicAnimation(withInitialPostion initialPos: CGPoint, finalPos: CGPoint) -> CABasicAnimation {
    let basicAnimation = CABasicAnimation(keyPath: "position")
    basicAnimation.fromValue = NSValue(cgPoint: initialPos)
    basicAnimation.toValue = NSValue(cgPoint: finalPos)
    basicAnimation.duration = 1
    basicAnimation.isRemovedOnCompletion = false
    basicAnimation.fillMode = CAMediaTimingFillMode.forwards
    basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    return basicAnimation
}
เชี่ยวชาญ iOS Animations ด้วย CAAnimation: ผ่านความกลัว สร้าง Motion ที่ลื่นไหล
CA Basic Animation

สำหรับ Implementation นี้ ผมใช้ CABasicAnimation

Class CABasicAnimation ช่วย Animate Layer Property (Background Color, Opacity, Position, Scale) ระหว่างสองค่า คุณแค่ให้ Start และ End Value ส่วนที่เหลือมันจัดการ เริ่มทันทีใน Run Loop ถัดไป ตามที่อธิบายเพิ่มเติมที่นี่

กลับมาที่ปัญหาของเรา

เพื่อ Implement นี้ ผมเอา Image Views สองตัว ใส่ Images แยกต่างหาก แล้วทำแอนิเมชันทีละอันด้วย CAAnimation

Source Code อยู่ที่นี่

ถ้าสังเกต GIF ล่าสุด จะเห็นว่าบางอย่างผิดปกติ ก่อน Gift Box Image แรกจะหลุดออกจากมุมมอง Headphones จะกระพริบสั้นๆ แล้ว Image ก็เคลื่อนขึ้น

ทำไมถึงเกิด?

เพราะทันทีที่เราเพิ่ม Animation ให้ Image View เราก็เพิ่ม Next Image ให้ View นั้น (บรรทัด 5 และ 6):

private func addAnimation(firstImageView: UIImageView, secondImageView: UIImageView) {
    let basicAnimation1 = getBasicAnimation(withInitialPostion: centerPosition, finalPos: finalPosition)
    firstImageView.layer.add(basicAnimation1, forKey: "position")
    let basicAnimation2 = self.getBasicAnimation(withInitialPostion: self.initalPosition, finalPos: self.centerPosition)
    secondImageView.layer.add(basicAnimation2, forKey: "position")
    self.addNextImage(forImageView: firstImageView)
}

ที่นี่เรากำลังต่อสู้กับปัญหา Sync ทั้งสอง Images ในแอนิเมชัน แต่ก็มี Solution ด้วย CAAnimation

CA Transactions

CA Transactions ช่วย Sync Multiple Animations พร้อมกัน รับรองว่า Animations ทั้งหมดที่ Bundle ไว้ด้วยกัน เริ่มพร้อมกัน

นอกจากนี้ คุณสามารถให้ Completion Block กับ Animations ได้ ซึ่งจะ Execute เมื่อ Animations ทั้งหมดใน Bundle เสร็จ

อ่านเพิ่มเติมที่นี่

private func addAnimation(firstImageView: UIImageView, secondImageView: UIImageView) {
    CATransaction.begin()
    CATransaction.setCompletionBlock {
        self.addNextImage(forImageView: firstImageView)
    }
    let basicAnimation1 = getBasicAnimation(withInitialPostion: centerPosition, finalPos: finalPosition)
    firstImageView.layer.add(basicAnimation1, forKey: "position")
    CATransaction.commit()
    let basicAnimation2 = self.getBasicAnimation(withInitialPostion: self.initalPosition, finalPos: self.centerPosition)
    secondImageView.layer.add(basicAnimation2, forKey: "position")
}

เริ่มด้วย CATransaction.begin() จากนั้นเขียน Animations ทั้งหมดที่ต้องการ Sync สุดท้ายเรียก CATransaction.commit() ซึ่งจะเริ่ม Animation ใน Block

ลองดูแอนิเมชันตอนนี้:

เชี่ยวชาญ iOS Animations ด้วย CAAnimation: ผ่านความกลัว สร้าง Motion ที่ลื่นไหล
CA Transaction

อีกอย่างสุดท้าย ผมต้องเพิ่ม Spring Effect ให้แอนิเมชัน ขอขอบคุณ CAAnimation ที่มี Solution ให้

CA Spring Animation

CA Spring Animation เมื่อเพิ่มให้ Layer จะให้ Spring-like Effect ทำให้ดูเหมือนถูกดึงไปยัง Target โดย Spring

Layer อยู่ห่างจาก Target มากเท่าไร Acceleration ไปทางนั้นก็มากเท่านั้น

อนุญาตควบคุม Physically-based Attributes เช่น Damping และ Stiffness ของ Spring – Docs

อ่านเพิ่มเติมจาก Apple Documentation: https://developer.apple.com/documentation/quartzcore/caspringanimation

มา Implement กับโค้ดเดิม:

private func getSpringAnimation(withInitialPostion initialPos: CGPoint, finalPos: CGPoint) -> CASpringAnimation {
    let basicAnimation = CASpringAnimation(keyPath: "position")
    basicAnimation.fromValue = NSValue(cgPoint: initialPos)
    basicAnimation.toValue = NSValue(cgPoint: finalPos)
    basicAnimation.duration = basicAnimation.settlingDuration
    basicAnimation.damping = 14
    basicAnimation.initialVelocity = 5
    basicAnimation.isRemovedOnCompletion = false
    basicAnimation.fillMode = CAMediaTimingFillMode.forwards
    return basicAnimation
}
เชี่ยวชาญ iOS Animations ด้วย CAAnimation: ผ่านความกลัว สร้าง Motion ที่ลื่นไหล
Voila

งานของผมเสร็จแล้ว

สรุป ข้อดีของการใช้ CA Animations:

  • ใช้ง่ายและ Implement ง่าย
  • มี Customizations เยอะ
  • Sync Multiple Animations ได้
  • CPU Usage เกือบศูนย์

นี่เพียงข้อดีบางส่วน ความเป็นไปได้ไม่มีที่สิ้นสุด

ตอนนี้ ทุกครั้งที่มี Requirement เรื่อง Animation ผมมั่นใจ Design และ Implement ได้ และหวังว่าคุณก็รู้สึกเหมือนกันหลังอ่าน
อย่าลังเล Leave Suggestions หรือ Feedback