LoginSignup
2
0

More than 3 years have passed since last update.

CAMetalLayerを実機でもシミュレータでも良い感じにビルドする

Last updated at Posted at 2020-04-02

時々、とても奇妙なクラスに出会うことがある。
CAMetalLayerはまさにそれで、実機ではiOS8+、シミュレータではiOS13+が必要になる。
これはシミュレータでMetalが使えるようになったのがiOS13~だったからという歴史的経緯によるものだ。
ちなみに内部的には実機とシミュレータでSDKが別れているため、それぞれのSDKごとにavailableが定義されて実現している。

実機SDK
API_AVAILABLE(macos(10.11), ios(8.0), watchos(2.0), tvos(9.0))
@interface CAMetalLayer : CALayer
シミュレータSDK
@available(iOS 13.0, *)
open class CAMetalLayer : CALayer {

さて、問題はアプリでCAMetalLayerを利用するときで、iOS12未満もサポートしているアプリだと次のような挙動になる。

実機 シミュレータ
iOS12 コンパイルエラー
iOS13

この挙動をどう見るか難しいところだが、シミュレータ相手であればさっとコンパイルだけ通したいこともあると思う。
そんなときは

public protocol CAMetalLayerInterface: class {
  var pixelFormat: MTLPixelFormat { get set }
  var framebufferOnly: Bool { get set }
  var presentsWithTransaction: Bool { get set }
  func nextDrawable() -> CAMetalDrawable?
}
#if targetEnvironment(simulator)
@available(iOS 13, *)
extension CAMetalLayer: CAMetalLayerInterface {}
#else
@available(iOS 12, *)
extension CAMetalLayer: CAMetalLayerInterface {}
#endif
open class AnimationView: UIView {
  typealias LayerClass = CAMetalLayerInterface & CALayer
  override open class var layerClass: Swift.AnyClass {
    #if targetEnvironment(simulator)
    if #available(iOS 13, *) {
      return CAMetalLayer.self
    } else {
      preconditionFailure("Not support simurator older than iOS12.")
    }
    #else
    return CAMetalLayer.self
    #endif
  }
  private var gpuLayer: LayerClass { self.layer as! LayerClass }
}

こんな感じで型を消しつつ、iOS12 & Simulator以外のときに型を入れてあげるのが良い気がする。

#if canImport(QuartzCore.CAMetalLayer)

とか

#if targetEnvironment(simulator) && available(iOS 13, *)

とか出来ればいいのになーって思ってしまった。レアケースだけど…

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0