【Swift】コードで透明度を設定する


はじめに

個人開発をしていてハマったのでまとめておきます。

alpha

alphaについてです。
・0.0から1.0のCGFloat型
・0.0: 完全に透明 (transparent)
・1.0: 完全に不透明 (opaque)
・viewを透過する
・subView全てに影響

opacity

opacityについてです。
・0.0から1.0のFloat型
・0.0: 完全に透明 (transparent)
・1.0: 完全に不透明 (opaque)
・色を透過する

View自体の透過度を変更する(Viewのalpha)

View自体の透過度を変更するには、以下のように、alpha値を設定します。

myView.alpha = 0.4

しかし、これだと以下のようにサブビューであるUILabelにまで透過の影響が出てしまいます。(赤色のUIViewがmyView)

Viewの背景色の透過度を変更する(色のalpha)

先ほどのように、サブビューには透過を影響させたくない時は以下のように、背景色にalpha値を指定してあげます。こうすることで、myViewの背景色のみ透過し、サブビューはその影響を受けなくなります。

myView.backgroundColor = UIColor(red: 0, green: 200, blue: 100, alpha: 0.4)

こちらのような書き方も同様の挙動を見せます。
withAlphaComponentはすでにUIColorを作成しており、alpha値だけ変更したい!って時に便利です。

myView.backgroundColor = .red.withAlphaComponent(0.4)

View自体の透明度を出力する

これは簡単ですが、出力される値はまちまちです。環境や設定した値によって誤差が含まれ、キリのいい値は出てくれないことが多いです。(1.0や0.5などは例外)
これについては後述します。

myView.alpha = 0.4
print(myView.alpha) // 0.4000000059604645

Viewの背景色の透明度を出力する

こちらはcgColorを使う必要があります。(これはキリのいい数値になるのはなぜ、、、)

myView.backgroundColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.4)
print(myView.backgroundColor!.cgColor.alpha) // 0.4

ちょっと実験と考察

opacityやalphaを設定することで挙動がどう変わるのか、はたまた同じ挙動なのかを色々実験してみます。
また、storyboardでalphaやopacityは今回変更していないため、storyboard上はmyViewのalphaは1、opacityは100%になっています。(コードで変更しているため、これらを変えても出力結果は同じになりますが、上書きされるという解釈で構いません。)

alphaを変更するとopacityは影響するのか

myView.alpha = 0.4
print("alpha:", myView.alpha)
print("opacity:", myView.layer.opacity)
// alpha: 0.4000000059604645
// opacity: 0.4

alphaを変更するとopacityもalphaを設定した値になりました。ちなみに、opacityのみを変更してもalphaの出力も影響されます。
さて、alphaの出力結果のみ有効数字が大きくなっています。これについては、型の違いによる精度の違いが大きく影響しています。
alphaの型はCGFloat型で、opacityの型はFloat型です。この型の違いによって出力結果が微妙に変わってしまいます。
先ほどCGFloatは環境によって変わると言いました。Macのビット数が32ビットなのか64ビットなのかによって変わります。Appleは10年以上前から64ビットを発売しているので、おそらく、皆さんの環境も64ビットだと思います。64ビットでビルとする場合、CGFloatはDouble型の浮動小数点型になります。32ビットだとFloatになります。つまり、64ビット環境だとCGFloatはDoubleのtypealias(置き換え)です。おそらく、両環境でもビルドできるようにこのような型が追加されたものだと推測しています。

つまり、Double型(64ビット)とFloat型(32ビット)の違いというわけです。
Floatは最小で6桁の精度なのに対し、Doubleは最小で15桁の精度です。
今回、opacityの桁がキリよく現れたのはopacityがFloat型で小数点以下6桁に0以外の数字が現れなかったため、有効数字が下二桁目で切られたと考えられます。Double型のalphaはしっかり少数第15位まで表示されています。

alphaとopacity両方変更するとどうなるのか

myView.layer.opacity = 0.3
myView.alpha = 0.5
print("alpha:", myView.alpha)
print("opacity:", myView.layer.opacity)
// alpha: 0.5
// opacity: 0.5

最後に設置された値になるみたいですね。(掛け算とかされてくれれば面白かったのですが、、、)

背景色の透明度を変更することでviewの透明度は影響を受けるのか

myView.backgroundColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.4)
print("背景alpha:", myView.backgroundColor!.cgColor.alpha)
print("alpha:", myView.alpha)
print("opacity:", myView.layer.opacity)
// 背景alpha: 0.4
// alpha: 1.0
// opacity: 1.0

背景のalphaを変更してもview自体のalphaやopacityはデフォルト値のまま(影響を受けない)であることがわかりました。

cgColorというものが出てきました。直感的には以下のように書けそうです。

print(myView.backgroundColor!.alpha)

しかし、これだとValue of type 'UIColor' has no member 'alpha'とXcodeに怒られます。
cgColorとは何なのかを簡単にまとめます。
ドキュメントにはカラーオブジェクトに対応するクォーツカラーと書かれており、以下のように宣言されています。

var cgColor: CGColor { get }

つまり、CGColorクラスを型に持つUIColorのプロパティです。これによってCGColorとCIColorにアクセスできるようになります。
よって、cgColorを使うことでUIKitで定義されているクラスなどをCGColorと結び付け、CGColorクラスで定義されているalphaなどにアクセスできるようにしています。

ここで、CGColor(cgColorではない)についても簡単に説明しておきます。UIColorという似たものがありますが、これらは少し異なります。
CGColorはCore Graphics Frameworkというフレームワーク内のクラスの一つです。これはiOSやMacなど共通で扱えるものです。しかし、UIColorはUIKit Frameworkというお馴染みのフレームワーク内のクラスの一つです。UIKitはiOS専用のため、Macアプリを開発したいってなった時は使うことができません。Macアプリを開発するときはAppKit FrameworkというUIKitとは違うAppKitを使います。これらを統一しない理由はiPhone(iPad)はCPUの性能がMacに比べて性能が低く、画面サイズもmacと比べると小さいです。さらに、macはキーボードで操作するのに対し、iPhoneやiPadはタップしてユーザーが操作します。これらの違いにより、AppKitの代わりにUIKitが必要になってきます。しかし、アニメーションやグラフィックはmacでもiOSでも違いはありません。これらのことから、レイヤーはiOSやmacと共通で使えるCGColorを使い、UIViewはUIKitで定義されているクラスなので、UIKitで定義されているUIColorの方が使いやすく、UIViewの背景色を変更するときなどはUIColorを使います。

おわりに

考察間違っていましたら、ご指摘ください!