質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

327閲覧

Swift コードでlabelを記述、レイアウト用の別のクラスから参照したい

ataru2222

総合スコア272

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2022/05/15 01:52

編集2022/05/16 11:20

実現したいこと

現在コードでlabelを記述するようにしているのですが、別のクラス(Layout.swift)から参照したい。

ViewControllr内にfunctionとして書き出すことはできたのですが、パーツが多くなってくるとどうしても縦長になってしまうのでレイアウトに関する記述は、可能ならばレイアウト用のファイルに書きたいと考えております。

発生している問題・エラーメッセージ

Instance member 'label' cannot be used on type 'ViewController'; did you mean to use a value of this type instead?

訳:
インスタンスメンバー「label」はタイプ「ViewController」では使用できません。 代わりにこのタイプの値を使用するつもりでしたか?

該当のソースコード

swift

1import UIKit 2 3class ViewController: UIViewController { 4 5 let label = UILabel() 6 let button1 = UIButton() 7 8 override func viewDidLoad() { 9 super.viewDidLoad() 10 11 Layout.testLabel() 12 } 13}

swift

1import Foundation 2class Layout { 3 4 //ラベルを配置 5 class func testLabel() { 6 7 ViewController.label.text = "Label" 8 ViewController.label.frame = CGRect(x: 0, y: 0, width: 100, height: 100) 9 10 ViewController.view.addSubview(ViewController.label) 11 12 } 13} 14

試したこと

ViewController内にfunctionとして書き出すことはできたが、別クラス(Layout.swift)では上手くいかず。

このようなことは可能でしょうか?
詳しい方いらっしゃいましたら、ご教授願います。
よろしくお願いいたします。

追記2022.05.16 20:16

swift

1import Foundation 2class Layout { 3 4 //ラベルを配置 5 func testLabel() { 6 let test = ViewController() 7 test.label.text = "Label" 8 9 test.label.frame = CGRect(x: 100 //←ここでスコープエラー 10 , y: 100 11 , width: 100 12 , height: 100) 13 14 test.view.addSubview(test.label) 15 16 } 17}

少し進んだのですがCGRectにアクセスできない状態です。
let testView = UIView()
var testView = UIView()
let label → var label

等を思いついたのですが、CGRectはどうすれば通して、配置で切るのでしょうか?
ご存知の方いらっしゃいましたら、ご教授願います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2022/05/16 01:54

クラスとインスタンスの区別がついてないようですね。 label はインスタンスごとに存在する変数なので、label にアクセスするには ViewController のインスタンスが必要です。
ataru2222

2022/05/16 11:25

hoshi-takanori様 回答していただき、ありがとうございます。 確かにインスタンス化してみたら一部のエラーは消えたのですが、CGRectの行だけ修正できませんでした。 業務で書くとき、このような事をすることはありますか? ご存知でしたら、教えていただければ幸いです。
hoshi-takanori

2022/05/16 15:35

追記見ました。「インスタンスが必要」と言われたら、やっぱりそうなりますよね…。残念ながら、testLabel メソッドで let test = ViewController() とすると、元の ViewController インスタンスとは別に、新しいインスタンスを作ってるので、label も view も別物になってしまいます。新しくインスタンスを作るのではなく、今あるインスタンス (self) を、引数で渡すのが良いかと…。
ataru2222

2022/05/18 15:17

hoshi-takanori様 >今あるインスタンス (self) を、引数で渡すのが良いかと…。 こちらについて調べてみたのですが、 let test = ViewControllerA(popType: ViewController) のような記述はあったのですが、動かずよく分かりませんでした。 今回のような場合、どのインスタンスを使って引数で渡したら良いのでしょうか?
guest

回答1

0

ベストアンサー

今回のような場合、どのインスタンスを使って引数で渡したら良いのでしょうか?

もちろん self です。


説明すると長くなりますが…、まず次のようなクラスがあるとします。

swift

1class Foo { 2 var name: String = "" 3 4 func hello() { 5 print("Hello \(name)") 6 } 7} 8 9var f1 = Foo() 10f1.name = "Taro" 11 12var f2 = Foo() 13f2.name = "Jiro"

f1 と f2 が違うオブジェクト (インスタンス) なのは分かりますよね。
f1.hello() とすれば Hello Taro、f2.hello() なら Hello Jiro と表示されるはずです。

次にこんな関数を考えます。

swift

1func g(foo: Foo) { 2 for i in 0..<3 { 3 foo.hello() 4 } 5}

g(foo: f1) や g(foo: f2) とすれば Hello Taro や Hello Jiro が 3 回表示されます。


それでは問題です。Foo にメソッドを追加して、自分自身を引数として g を呼び出すにはどうしたらいいでしょうか? つまりこんな感じです。

swift

1class Foo { 2 // name と hello は省略 3 4 func callG() { 5 // 自分自身を引数として g を呼び出す 6 } 7} 8 9f1.callG() // Hello Taro と 3 回表示される 10f2.callG() // Hello Jiro と 3 回表示される

callG の中身を考えましょう。g を呼び出すには Foo のインスタンスが必要です。

swift

1 func callG() { 2 g(foo: /* ここに Foo のインスタンスが必要 */) 3 }

インスタンスが必要なら、新しく作ってみましょうか。

swift

1 func callG() { 2 g(foo: Foo()) 3 }

これだと、f1.callG() としても f2.callG() としても Hello としか表示されません。Foo() というのは新しく作ったインスタンスで、name が何も設定されてない ("" のまま) だからです。

ここで出てくるのが self ってやつで、例えば Foo のメソッドの中で self と書くと、そのメソッドが実行されているインスタンス自身を指すことになります。

swift

1 func callG() { 2 g(foo: self) // f1.callG() の場合、self は f1 なので、Hello Taro と 3 回表示される 3 // f2.callG() の場合、self は f2 なので、Hello Jiro と 3 回表示される 4 }

関数の代わりに他のクラスを作る場合でも同様です。

swift

1class Foo { 2 // name と hello は省略 3 4 func callG() { 5 let bar = Bar(foo: self) 6 bar.g() 7 } 8} 9 10class Bar { 11 var foo: Foo 12 13 init(foo: Foo) { 14 self.foo = foo 15 } 16 17 func g() { 18 for i in 0..<3 { 19 foo.hello() 20 } 21 } 22}

Bar のイニシャライザ (init) に self.foo = foo というのが出てきますが、この self も同様に自分自身を指します。init はインスタンスが作られるときに呼ばれる特殊なメソッドのようなもので、let bar = Bar(foo: self) で Bar のインスタンスを作るときに呼ばれて、左辺の self.foo の self 今まさに作られた Bar のインスタンスを指し、その foo プロパティに右辺の foo を代入します。なお、右辺の foo は引数で与えられた Foo のインスタンス (callG では self を渡してますが、Foo のメソッドなので、この self は Foo のインスタンス) になります。


やっと本題に入りますが、ViewController の処理の一部を Layout にやらせたいってことですよね。Layout の class メソッドにするなら、普通の関数と同様の使い方になるので、毎回 ViewController のインスタンス (self) を引数で渡すことになります。

ちなみに、ViewController のインスタンスはどこから湧いてきたの? という疑問があるかもしれませんが、たぶん storyboard を使ってると思いますので、アプリ起動時に iOS が main.storyboard から最初の画面を自動的に作ってくれる中に含まれるので、自分で ViewController のインスタンスを作る必要はありません。(画面遷移をコードで記述する場合には、遷移先の画面の ViewController を自分で作る場合もあります。)

swift

1class ViewController: UIViewController { 2 let label = UILabel() 3 let button1 = UIButton() 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 8 Layout.testLabel(vc: self) 9 } 10} 11 12class Layout { 13 //ラベルを配置 14 class func testLabel(vc: ViewController) { 15 16 vc.label.text = "Label" 17 vc.label.frame = CGRect(x: 0, y: 0, width: 100, height: 100) 18 19 vc.view.addSubview(vc.label) 20 } 21}

ただ、class メソッドを多用するのは良くないと個人的には思っていて、この場合 Layout クラスにメソッドが増えたときに毎回 ViewController を引数で渡したり、呼ぶたびに Layout のインスタンスを作るのはいけてない気がするんですが、Layout を ViewController のプロパティにすると循環参照の問題が生じるので weak 参照を使うことになるでしょうね…。

投稿2022/05/21 02:02

編集2022/05/21 02:04
hoshi-takanori

総合スコア7895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ataru2222

2022/05/23 09:10

こんなに丁寧に書いていただき、ありがとうございます(涙) このような書き方が出来る事、本当に勉強になりました。そしてあまりコードとしてはイケてないのですね(笑) まだまだ、勉強不足なのでそういう所も分かるように精進していきます。 解答していただきまして、本当にありがとうございました。
hoshi-takanori

2022/05/23 09:24

長々と書きましたが、単に ViewController のソースが長くなって分割したいだけなら、extension を使う方法もあります。 https://qiita.com/Yaruki00/items/a0eee0a9050c410bd266 あと、レイアウトに関しては、iPhone の画面サイズのバリエーションもだいぶ増えたので、固定値とか自分で計算するとかより、Auto Layout をちゃんと使うのがお勧めです。コードで書くと冗長なので、SnapKit あたりを使うと良いかも。 https://qiita.com/turara/items/bea0c41f8b5f26b04b20
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問