LoginSignup
10
8

More than 5 years have passed since last update.

frame.size.width + 10をframe.size.width + marginにした途端に動かなくなる理由

Last updated at Posted at 2015-12-03

表題の現象ですが、Swiftを始めたばかりの頃は結構遭遇するのではないかと思います。
私は以前は良くハマっていました。

今回はなぜ数字を変数にしただけでエラーになるかを書いていきます。

エラーになる状況

下のコードは普通に動きます。
10はマジックナンバーなのでmarginと言った変数にしたいとします。

let x = view.frame.size.width + 10

しかし変数にした途端にエラーになってしまいます。

let margin = 10
// 変数化しただけでエラー、辛い(˘ω˘)
let x = view.frame.size.width + margin // `Binary operator '+' cannot be applied to operands of type 'CGFloat' and 'Int'` エラー

なぜ変数にするとエラーになるのか

これはview.frame.size.widthとmarginの型が違うからです。
view.frame.size.widthはCGFloat型でmarginはInt型です。
SwiftではCGFloatとIntの演算はサポートされていないのでエラーとなります。

CGFloat(10) + Int(10) // エラーになる

view.frame.size.width + 10はなぜエラーにならないか

CGFloatとIntの演算はエラーになるという事なので、view.frame.size.width + 10も型が違ってエラーになりそうです。
しかし実際はエラーは発生しません。

これは10は演算時にCGFloatに変換されるからです。

10がCGFloatに変換される仕組み

10がCGFloatに変換される仕組みですが、CGFloatが実装しているIntegerLiteralConvertibleというプロトコルが関係しています。

IntegerLiteralConvertibleを実装していると10などのIntリテラルが自動でそのクラスに変換されるようになります。
CGFloatはIntegerLiteralConvertibleを実装しているので、10はCGFloatの10.0に変換されます。

Intリテラルがそのクラスに変換されるとは?

Intリテラルがそのクラスに変換されると言う所についてもう少し深掘りします。

下の実装は型が違うためにエラーが起きます。

class MyClass {}

let myClass: MyClass = 1 // MyClass型に1を入れようとするとエラー

しかしMyClassにIntegerLiteralConvertibleを付けるとエラーが起きなくなります。

class MyClass: IntegerLiteralConvertible {
    typealias IntegerLiteralType = Int
    required init(integerLiteral value: Int) {}
}

let myClass: MyClass = 1

これはIntegerLiteralConvertibleを実装した為、1(Intリテラル)が自動でMyClass(integerLiteral: 1)に変換された為です。

let myClass: MyClass = 1
// ↑ は ↓ のように変換される
// let myClass: MyClass = MyClass(integerLiteral: 1)

CGFloatも下のように変換されています。

let value: CGFloat = 1
// ↑ は ↓ のように変換される
// let value: CGFloat = CGFloat(integerLiteral: 1)

まとめ(frame.size.width + 10がエラーにならない理由)

つまりview.frame.size.width + 10という計算の時、10はCGFloat(integerLiteral: 10)になるので問題なく動いているという事です。

let x = view.frame.size.width + 10
// ↓ のように変換されているのでエラーにならない
// let x = view.frame.size.width + CGFloat(integerLiteral: 10)

逆に変数にしてしまうと、IntリテラルではなくInt型になるのでCGFloatへの自動変換は行われなくなります。

let margin = 10 // → marginはInt型

その為変数にする際はキャスト or 型宣言を付ける必要があります。

let margin: CGFloat = 10

参考URL

10
8
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
10
8