忘備録-iOSのフレームワークとアニメーション

趣味でiOSアプリ開発をかじっていた自分が、改めてiOS開発を勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。

## 参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

- [本気ではじめるiPhoneアプリ作り Xcode 10.x対応 (Informatics&IDEA)](https://www.amazon.co.jp/%E6%9C%AC%E6%B0%97%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8BiPhone%E3%82%A2%E3%83%97%E3%83%AA%E4%BD%9C%E3%82%8A-Xcode-10-x%E5%AF%BE%E5%BF%9C-Informatics-IDEA/dp/4815600031/)



##フレームワーク
フレームワーク : 目的別にまとめた機能の集まり。
UIKitもフレームワーク。

iOSが用意しているフレームワークは大きく４つのレイヤーに分かれている。

Cocoa Touch : iOSアプリを作成するための主要なフレームワークが含まれる。アプリのUI、タッチ操作、プッシュ通知など。
Media : サウンド、グラフィックス、オーディオなど。
Core Services : アプリの基本的なサービスに必要な機能。位置情報の取得、ソーシャル機能など。
Core OS : テクノロジーの土台となる機能。外部とのハードウェアとの通信を明示的に扱う際などに使う。

##フレームワークとサイズ
公式のフレームワークを使ってもアプリのサイズは変わらない。
iPhoneの実機に最初から組み込まれている。しかしiOSのバージョンによって使えないものもある。
例えばiCloudを操作する機能CloudKitはiOS8からしか搭載されていないので、iOS7以下の実機だと動作しない。

####フレームワークをimportする理由
フレームワークをimportすると、アプリのビルド時間が長くなり、アプリの起動時間が遅くなるので必要なものだけをできるだけ絞って使うのがいい。




##iOSのアニメーション処理
iOSではCore Animationと呼ばれるアニメーションの基盤機能を利用して画面の動きを実現している。
Core Animationを利用するとビューの状態変化をアニメーションで表現できる。
Core Animationは一度ビューをビットマップ画像に変換し、グラフィック処理用のハードウェアでアニメーションの描画処理を行なっている。

##UIViewのレイヤー
レイヤーはアニメーションの視覚的処理を管理している。
レイヤーはビューに性質がよく似た軽量オブジェクト。
ビューの視覚的な情報をプロパティとして持っている。


##簡単なアニメーションを実装1
ボタンをタップした時Viewの角を丸くする。

![角丸アニメーション.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542614/e66dcfc0-d980-ff73-e1d8-18608f0604d9.gif)


storyboard
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542614/0dfd7437-a106-88f8-8e8a-a439e967060b.png)


```swift:ViewController
import UIKit

enum AnimationKey: String {
  case cornerRadius
}

class ViewController: UIViewController {

  @IBOutlet weak var greenView: UIView!
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
  }

  @IBAction func tapStartButton(_ sender: Any) {
    viewToRound(view: greenView, duration: 3.0)
  }
  
  func viewToRound(view: UIView, duration: Double = 1.0) {
    let animation = CABasicAnimation(keyPath: AnimationKey.cornerRadius.rawValue)

    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
    animation.fromValue = 0
    animation.toValue = 100
    animation.duration = duration
    view.layer.add(animation, forKey: AnimationKey.cornerRadius.rawValue)
    view.layer.cornerRadius = 100
  }  
}
```

処理のメモ

```swift
    // インスタンスを生成する際に対象となるプロパティを指定する。
    // 角を丸めるアニメーションを実施するので文字列でcornerRadiusを指定する。
    let animation = CABasicAnimation(keyPath: AnimationKey.cornerRadius.rawValue)
     
    // アニメーション変化のタイミングを指定
          /* CAMediaTimingFunctionのインスタンス生成時に指定するプロパティは
          default:標準, linear:一定速度, easeIn:段々速度上昇, easeOut:段々速度減少,
          easeInEaseOut:速度上昇後速度減少 */
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
        
    // アニメーション開始時の値
    animation.fromValue = 0
    
    // アニメーション終了時の値
    animation.toValue = 100
    
    
    // アニメーション時間
    animation.duration = duration
    
    // 作成したアニメーションのインスタンスをレイヤーに設定
    // 角を丸めるアニメーションを実施するので文字列でcornerRadiusを指定する。
    view.layer.add(animation, forKey: AnimationKey.cornerRadius.rawValue)
    
    // アニメーション後のViewの値を指定する。指定しないと元の値のまま表示される。
    view.layer.cornerRadius = 100
```

##簡単なアニメーションを実装2
ボタンを押すたびに画像を表示してゆっくり消していく。

(AudioToolBoxなどをimportして画像を表示するときに音を鳴らしたかった)

![音符表示アニメーション.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542614/65999935-5d0a-6032-69cd-a78230010823.gif)



storyboard
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542614/61912011-031c-a467-0b68-bc39927a6c9e.png)



```swift
import UIKit

enum Icon: String {
  case icon0 = "noteIcon0"
  case icon1 = "noteIcon1"
  case icon2 = "noteIcon2"
  case icon3 = "noteIcon3"
  case icon4 = "noteIcon4"
  case icon5 = "noteIcon5"
}

class IconViewController: UIViewController {
  @IBOutlet weak var iconImageView: UIImageView!
  @IBOutlet weak var button: UIButton!
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
  }
  
  @IBAction func buttonTap(_ sender: Any) {
    self.button.isEnabled = false
    setIcon()
  }
  
  func setIcon(){
    let randomInt = Int.random(in: 0...5)
    
    switch randomInt {
    case 0:
      iconImageView.image = UIImage(named:Icon.icon0.rawValue)
    case 1:
      iconImageView.image = UIImage(named:Icon.icon1.rawValue)
    case 2:
      iconImageView.image = UIImage(named:Icon.icon2.rawValue)
    case 3:
      iconImageView.image = UIImage(named:Icon.icon3.rawValue)
    case 4:
      iconImageView.image = UIImage(named:Icon.icon4.rawValue)
    case 5:
      iconImageView.image = UIImage(named:Icon.icon5.rawValue)
    default:
      iconImageView.image = UIImage(named:Icon.icon1.rawValue)
    }
    
    // アニメーション処理
    UIView.animate(withDuration: 2.0, animations: {
      self.iconImageView.alpha = 0
    }, completion: { (Bool) in
      self.iconImageView.image = nil
      self.iconImageView.alpha = 1.0
      self.button.isEnabled = true
    })
  }

  func reloadView(){
    // モーダルを使わない明示的な画面遷移
    if let nextIconViewController = storyboard?.instantiateInitialViewController() as? IconViewController{
      nextIconViewController.modalPresentationStyle = .fullScreen
      present(nextIconViewController, animated: false, completion: nil)
    }
  }
  
}
```