とりあえず、後半のご質問である「また試しに使ってみたところoverride func viewDidLoad()の外で記述すると警告文が出てきて実行できません。」への回答です。
このエラーがが出る理由ですが、(おそらく)screenWidth
や screenHeight
の宣言を「クラスのトップレベル(内部)」で行なっていることだと思います。
クラスのトップレベルにおけるプロパティ(変数)は、全てのプロパティの初期化が終了するまで(実質、イ全てのイニシャライザが呼び出され、インスタンスが生成されるまで)、相互に参照することができません(計算型プロパティなど、例外もあります)。
どういうことかというと、
Swift
1 let screenWidth = Int(UIScreen.main.bounds.size.width)
2 let screenHeight = Int(UIScreen.main.bounds.size.height)
と宣言されていますが、これら2つの変数は続くクロージャ内部での処理
Swift
1
2 let collectionView = UICollectionView( frame: CGRect(x: 0, y: 0, width: screenWidth * 0/100, height: screenHeight ), collectionViewLayout: layout)
3 //エラー:Instance member 'screenHeight' cannot be used on type 'ViewController'; did you mean to use a value of this type instead?
で呼び出されているため、表記のようなエラーが出てしまいます。
ここで再度、参考にされたQiitaの記事を見ていただきたいのですが、screenWidth
や screenHeight
ともにクラス外部のグローバル変数として宣言されていることに気づかれるかと思います。
Swift
1let screenSize: CGSize = CGSize(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
2
3final class ViewController: UIViewController {
4
5 private let collectionView: UICollectionView = {
6 // 中略
7 let collectionView = UICollectionView( frame: CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height ), collectionViewLayout: layout)
8 collectionView.backgroundColor = UIColor.white
この場合にはエラーが出ません。なぜかというと、ViewController
というクラスを宣言する前に、グローバル変数として既にscreenSize
が決定してしまっているからです。
なので、グローバル変数にすれば全て解決かというと、それほど単純な問題でもありません。
一般的に、グローバル変数を利用することによって、プログラム全体の見通しが悪くなる可能性があるためです。
ここでいう「見通しが悪くなる」というのは、「グローバル変数がどこで定義されたものか分からなくなってしまう」という意味です。クラスのプロパティとして定義してあれば、宣言したクラスやその拡張(extension
)をみればわかりますが、グローバル変数としてしまうと、最悪関連する全てのファイルを探し回る必要も出てきてしまいます。
そこで、クラス内部のトップレベルで定義したプロパティを、別のトップレベルで宣言したプロパティ(やクロージャ)で使うための方法として使われる一つの方法が「遅延格納型プロパティ(lazy
キーワード)」です。
遅延格納型プロパティというのは、クラスのトップレベルで宣言するものの、実際にその値が評価されるのは実際に使われる段階になってからです。実際に使われる段階だと、インスタンスは既に生成されている状態で、全てのプロパティに初期値が入っていることが保証されている(というか、保証できるように自分でクラスを設計する)ため、問題とならないわけです。
具体的にはこのような感じで宣言し、使います。
Swift
1class ViewController: UIViewController {
2 //スクリーンの横幅、縦幅を定義
3 let screenWidth = UIScreen.main.bounds.size.width
4 let screenHeight = UIScreen.main.bounds.size.height
5
6 // lazyキーワードを付与した上、let ではなく var として宣言する
7 lazy var collectionView: UICollectionView = {
8 // 中略
9 let collectionView = UICollectionView( frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight ), collectionViewLayout: layout)
10 // 攻略
11 return collectionView
12 }()
このような方法を取れば、コンパイル時にエラーが出てくることはなくなります(が、遅延評価であることを考慮しなければ、予想外の結果がでるかもしれません。それについてはぜひいろいろ実験してみてください)。
「簡単なコード(tableviewやcollectionviewを生成するだけ)でクロージャを使う例を見かけることがあるのですが、一体どんなことに役立つのでしょうか」ということについては、私は適切な答えを持ち合わせていないため、他の方のご意見も伺った方がいいかと思います。
一つ考えられるのは、インスタンスを代入させたいプロパティの宣言と、そこで実際に処理させたい内容を同時に記述させることができるため、全体の処理が見やすくなる、ということでしょうか。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/10/02 07:59
2020/10/02 08:30