Xin chào các bạn ! Hôm nay mình xin giới thiệu với các bạn một số những chuyển động hoạt hình (Animations) cơ bản trong Swift.
https://github.com/soapyigu/Swift-30-Projects/tree/master/Project%2011%20-%20Animations

B1: Tạo NavigationController, ViewController

Tạo ViewController

Để tạo Navigation, chọn màn ViewController, vào Editor - Embed in - Navigation Controller

Ta được như hình

Ở ViewController thứ 2 đổi tên thành Animations, tạo TableView cho màn này.

Màn thứ 3 đặt tên là Detail View Controller, thêm 1 Button tên là Animate

Để từ màn 2 show ra màn 3, ta chọn màn 2 (Animations) kéo vào màn 3 (Detail View Controller), chọn Show

Và đừng quên layout cho các thành phần trong các View chúng ta vừa kéo thả nhé !
Cuối cùng ta dc như hình dưới đây.

B2: Tiếp theo chúng ta cùng nhau vào phần code nào !
- Để ánh xạ TableView ta kéo chuột phải từ tableview vào phần code bên phải, chọn new referencing outlet


  class ViewController: UIViewController {
  // MARK: - IBOutlets
  @IBOutlet weak var masterTableView: UITableView!
  

- Khai báo các biến. Ở đấy ta có 8 ví dụ, nên khai 8 biến tương ứng

// MARK: - Variables
  fileprivate let items = ["2-Color", "Simple 2D Rotation", "Multicolor", "Multi Point Position", "BezierCurve Position",
                       "Color and Frame Change", "View Fade In", "Pop"]

- Tạo hàm animateTable() để tạo animation cho TableView.

func animateTable() {
    masterTableView.reloadData()
    
    let cells = masterTableView.visibleCells
    let tableHeight = masterTableView.bounds.size.height
    
    // move all cells to the bottom of the screen
    for cell in cells {
      cell.transform = CGAffineTransform(translationX: 0, y: tableHeight)
    }
    
    // move all cells from bottom to the right place
    var index = 0
    for cell in cells {
      UIView.animate(withDuration: duration, delay: 0.05 * Double(index), usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: [], animations: {
        cell.transform = CGAffineTransform(translationX: 0, y: 0)
        }, completion: nil)
      index += 1
    }
  }

- Tạo Segue để chuyển đổi giữa 2 màn ViewController chúng ta tạo lúc đầu là AnimationsDetailViewController.

// MARK: - Segue
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == segueDetailIdentifier {
      let detailView = segue.destination as! DetailViewController
      let indexPath = masterTableView.indexPathForSelectedRow
      
      if let indexPath = indexPath {
        detailView.barTitle = self.items[(indexPath as NSIndexPath).row]
      }
    }
  }
}

- Thiết lập UITableViewDelegate và UITableViewDataSource.

// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
  func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return CGFloat(headerHeight)
  }
  
  func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return "Basic Animations"
  }
}

// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
  func numberOfSections(in tableView: UITableView) -> Int {
    return 1
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return items.count
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellIdentifier = "cell"
    let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
    
    cell.textLabel?.text = self.items[(indexPath as NSIndexPath).row]
    
    return cell
  }
}

- Thêm 3 biến ngoài class ViewController

import UIKit

let headerHeight = 50.0
let segueDetailIdentifier = "toAnimateDetail"
let duration = 1.5

class ViewController: UIViewController {

- Đừng qên nhập hàm animateTable() vào ViewWillAppear
 

override func viewWillAppear(_ animated: Bool) {
    animateTable()
  }

- Tạo thêm 1 Swift file đặt tên là DetailViewController.

- Cài đặt NavigationBar.

import UIKit

class DetailViewController: UIViewController {

  // MARK: - Variables
  var barTitle = ""
  var animateView: UIView!
  fileprivate let duration = 2.0
  fileprivate let delay = 0.2
  fileprivate let scale = 1.2
  
  // MARK: - Lifecycle
  override func viewDidLoad() {
    super.viewDidLoad()
    setupRect()
    setupNavigationBar()
  }
  
  fileprivate func setupNavigationBar() {
    navigationController?.navigationBar.topItem?.title = barTitle
  }
  
  fileprivate func setupRect() {
    if barTitle == "BezierCurve Position" {
      animateView = drawCircleView()
      
    } else if barTitle == "View Fade In" {
      animateView = UIImageView(image: UIImage(named: "whatsapp"))
      animateView.frame = generalFrame
      animateView.center = generalCenter
    } else {
      animateView = drawRectView(UIColor.red, frame: generalFrame, center: generalCenter)
    }
    view.addSubview(animateView)
  }

- Ở màn Detail View Controller ta lại kéo thả Button Animate, chọn Touch Up Inside. Sau đó tiến hành code để được những Animations ưng ý.

// MARK: - IBAction
  @IBAction func didTapAnimate(_ sender: AnyObject) {
    switch barTitle {
    case "2-Color":
      changeColor(UIColor.green) // Đổi màu
      
    case "Simple 2D Rotation":
      rotateView(Double.pi) // Xoay 2 chiều
      
    case "Multicolor":
      multiColor(UIColor.green, UIColor.blue) // Nhiều màu
      
    case "Multi Point Position":
      multiPosition(CGPoint(x: animateView.frame.origin.x, y: 100), CGPoint(x: animateView.frame.origin.x, y: 350)) // Di chuyển vị trí
      
    case "BezierCurve Position":
      var controlPoint1 = self.animateView.center
      controlPoint1.y -= 125.0
      var controlPoint2 = controlPoint1
      controlPoint2.x += 40.0
      controlPoint2.y -= 125.0;
      var endPoint = self.animateView.center;
      endPoint.x += 75.0
      curvePath(endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2) //Làm quả bóng nảy
  
      
    case "Color and Frame Change":
      let currentFrame = self.animateView.frame
      let firstFrame = currentFrame.insetBy(dx: -30, dy: -50)
      let secondFrame = firstFrame.insetBy(dx: 10, dy: 15)
      let thirdFrame = secondFrame.insetBy(dx: -15, dy: -20)
      colorFrameChange(firstFrame, secondFrame, thirdFrame, UIColor.orange, UIColor.yellow, UIColor.green) // Đổi màu và cả kích thước
      
    case "View Fade In":
      viewFadeIn() // Đổi hình
      
    case "Pop":
      Pop()// nảy
      
    default:
      let alert = makeAlert("Alert", message: "The animation not implemented yet", actionTitle: "OK")
      self.present(alert, animated: true, completion: nil)
    }
  }
// MARK: - Private Methods for Animations
  fileprivate func changeColor(_ color: UIColor) {
    UIView.animate(withDuration: self.duration, animations: {
      self.animateView.backgroundColor = color
      }, completion: nil)
  }
  
  fileprivate func multiColor(_ firstColor: UIColor, _ secondColor: UIColor) {
    UIView.animate(withDuration: duration, animations: {
      self.animateView.backgroundColor = firstColor
      }, completion: { finished in
        self.changeColor(secondColor)
    })
  }
  
  fileprivate func multiPosition(_ firstPos: CGPoint, _ secondPos: CGPoint) {
    func simplePosition(_ pos: CGPoint) {
      UIView.animate(withDuration: self.duration, animations: {
        self.animateView.frame.origin = pos
      }, completion: nil)
    }
    
    UIView.animate(withDuration: self.duration, animations: {
      self.animateView.frame.origin = firstPos
      }, completion: { finished in
        simplePosition(secondPos)
    })
  }
  
  fileprivate func rotateView(_ angel: Double) {
    UIView.animate(withDuration: duration, delay: delay, options: [.repeat], animations: {
      self.animateView.transform = CGAffineTransform(rotationAngle: CGFloat(angel))
      }, completion: nil)
  }
  
  fileprivate func colorFrameChange(_ firstFrame: CGRect, _ secondFrame: CGRect, _ thirdFrame: CGRect,
                                _ firstColor: UIColor, _ secondColor: UIColor, _ thirdColor: UIColor) {
    UIView.animate(withDuration: self.duration, animations: {
      self.animateView.backgroundColor = firstColor
      self.animateView.frame = firstFrame
      }, completion: { finished in
        UIView.animate(withDuration: self.duration, animations: {
          self.animateView.backgroundColor = secondColor
          self.animateView.frame = secondFrame
          }, completion: { finished in
            UIView.animate(withDuration: self.duration, animations: {
              self.animateView.backgroundColor = thirdColor
              self.animateView.frame = thirdFrame
              }, completion: nil)
        })
    })
  }
  
  fileprivate func curvePath(_ endPoint: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint) {
    let path = UIBezierPath()
    path.move(to: self.animateView.center)
    
    path.addCurve(to: endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)

    // create a new CAKeyframeAnimation that animates the objects position
    let anim = CAKeyframeAnimation(keyPath: "position")
    
    // set the animations path to our bezier curve
    anim.path = path.cgPath
    
    // set some more parameters for the animation
    anim.duration = self.duration
    
    // add the animation to the squares 'layer' property
    self.animateView.layer.add(anim, forKey: "animate position along path")
    self.animateView.center = endPoint
  }
  
  fileprivate func viewFadeIn() {
    let secondView = UIImageView(image: UIImage(named: "facebook"))
    secondView.frame = self.animateView.frame
    secondView.alpha = 0.0
    
    view.insertSubview(secondView, aboveSubview: self.animateView)
    
    UIView.animate(withDuration: duration, delay: delay, options: .curveEaseOut, animations: {
      secondView.alpha = 1.0
      self.animateView.alpha = 0.0
      }, completion: nil)
  }
  
  fileprivate func Pop() {
    UIView.animate(withDuration: duration / 4,
      animations: {
      self.animateView.transform = CGAffineTransform(scaleX: CGFloat(self.scale), y: CGFloat(self.scale))
      }, completion: { finished in
        UIView.animate(withDuration: self.duration / 4, animations: {
          self.animateView.transform = CGAffineTransform.identity
        })
    })
  }

Các bạn hãy thử thay các thuộc tính để được các Animation khác nữa nhé !
Oke và giờ ấn chạy thôi !