IT アプリ開発

【Swiftui】構造体Structのプロパティの指定

2022年4月1日

structではいろんな変数の指定方法を見かけます。
直接代入したり、他の変数の値やViewの状態によって格納する値を変更させたり、nestした形式で格納したりなど色々ありますが、
大きく分けて変数に代入する方法としては、2つあるかなと思います。

① =を用いて普通に代入する
② text:String{}のようにして、処理によって格納する値を変更させる。

プロパティの指定方法

以下の例では、
変数valueは、変数textの値によって格納する値を変更します。
test1という値であれば100を入れますが、それ以外の場合は0を入れます。
これはクロージャーと言って無名関数と言います。
これによって、処理をしてreturnさせることで代入を実現しています。

本来は以下のように書くこともできます。

状態によって変数に値を入れる処理を変えたりを、変数の指定の中でできたりもする。

struct aaaaa:View{
    

    public var pref:String = "Tokyo"
    
    public var text:String{
        "test"
    }

    public var value:Int{
        if(text == "test1"){
            return 100
        }
        return 0
    }

    public var value2:Int{
        get{
            if(text == "test1"){
                return 100
            }
            return 0
        }
        set{
            //value = 111
            value = value + 111
        }
    }

    var body: some view{
       Text("aaa")
       var _ = print()
    }
}

変数valueは、getが省略された形である。
value2ではgetとsetを定義している。
これでも動きます。

あれ?この見た目、プロパティラッパーを定義するときに使う形式に似ているが、
まさに、@propertywrapper定義しなくても、通常の変数にはこの機能があって、
1つ1つの変数にget、setが定義できる訳だが、
変数に対して共通の処理をかましたいというときに、独自のプロパティラッパーを作成して処理を行うことができる。

@PropertyWapper で変数を作成。
@propertyWrapper TestP{

get
set
}

そうすると
@TestP public var ss:String = ""
とかやるとき、ssに値を入れたり、取ろうとすると上で定義したget,setの処理をしてくれる
汎用的にいろんな変数に対して、できるのがいい

そしてプロパティラッパーは定義を見るとbindingが入っているので、デフォルトでビューの更新が走る。
そしてプロパティラッパーの処理タイミングについては、printでちゃんと確認した方がいいね

以下のようにいくつかの構造体でのプロパティ宣言の形があります。

import SwiftUI

struct TestPropertWrapper: View {
    
    public var pref:String = "Tokyo"
    public var text:String{
        "test"
    }
    public var pref_category:String{
        if(self.pref == "Tokyo"){
            return "東京"
        }
        return "東京以外"
    }
    
    public var value:Int{
        get{
            if(text == "test1"){
                return 100
            }
            return 0
        }
        set{
            self.value = self.value + 111
        }
    }
    
    var body: some View {
        VStack(){
            Button(action: {
                self.pref = "Saitama"           // Cannot assign to property: 'self' is immutable
            }, label: {
                Text("Test")
            })
            
            var _ = print(pref_category)        // 東京
            
            Button(action: {
                self.text = "aaa"               // Cannot assign to property: 'text' is a get-only property
            }, label: {
                Text("Test")
            })
            
            Button(action: {
                self.value = "aaa"              // Cannot assign to property: 'self' is immutable
            }, label: {
                Text("Test")
            })
        }
    }
}

● 1つめ
これは単純に

● 2つめ
これはちょっと=のほうがイメージつきやすいですが、
var text:String{

}
とすることで、クロージャを書くことができます。中はgetterで、returnするような処理になります。1行のみであればreturn省略して記載することができます。(画像の例)
そしてこの形式で書くことで、他のプロパティの値が◯○になったとき、このプロパティの値を変更させたりすることができるようになります。
それが3つ目になります。
1つ目でprefの値がTokyoであれば、東京を返し、Tokyo以外であれば東京以外を返します。

● 4つめ
2つめ、3つめはgetterのみの定義方法です。
それに対して、ここはget{}とset{}でgetter、setter両方を実装します。

でもそれぞれこれらを実行しようとするとエラーになります。
1つ目のエラーは、
Cannot assign to property: 'self' is immutable
です。
これは構造体では、プロパティの値を変更させることができません。
つまりsetter、直接値の代入をしてプロパティ値を更新させることができません。
そして、このエラーは、
4つめでも起きています。
4つめではsetterを定義していますが、プロパティの値を更新させることができません。

実は構造体structには、プロパティの値を変更することができない性質を持っています。
Viewプロトコルに従っているので、なんかプロパティ値を変えれそうって思ったかもしれません。Viewになってる訳だから、構造体じゃなさそうだし。
viewプロトコルに準拠したとしても、構造体は構造体です。
なので、普通に属性宣言をし、値を変更しようとしても(代入とか)エラーになってしまう。
Cannot assign to property: 'self' is immutable

構造体内で定義したメソッド内でストアドプロパティの値を変更する場合、mutatingの使用が必要になります。
以下のコードのようにイニシャライザで引数を参照して代入する時は必要ありませんが、メソッドで代入する時はmutatingの記述が必要で、もし記述がなくメソッドだけの場合はコンパイルエラーになるので注意してください。

構造体のプロパティに@Stateをつけてみる

struct TestPropertWrapper: View {
    
    @State public var pref:String = "Tokyo"
    @State public var text:String{            // Property wrapper cannot be applied to a computed property
        "test"
    }
    @State public var pref_category:String{   // Property wrapper cannot be applied to a computed property
        if(self.pref == "Tokyo"){
            return "東京"
        }
        return "東京以外"
    }
    
    @State public var value:Int{              // Property wrapper cannot be applied to a computed property
        get{
            if(text == "test1"){
                return 100
            }
            return 0
        }
        set{
            self.value = self.value + 111
        }
    }
    
    var body: some View {
        VStack(){
            Button(action: {
                self.pref = "Saitama"
            }, label: {
                Text("Test")
            })
            
            var _ = print(pref_category)
            
            Button(action: {
                self.text = "aaa"               // Cannot assign to property: 'text' is a get-only property
            }, label: {
                Text("Test")
            })
            
            Button(action: {
                self.value = "aaa"              // Cannot assign to property: 'self' is immutable
            }, label: {
                Text("Test")
            })
        }
    }
}

プロパティで宣言した値は、
ViewであればViewの中のいろんなところでその値を参照しています。
となると、値を代入などして更新をしたとしても、画面の再構築が行われなければ、値の更新は行われません。
構造体は参照型ではなく値型になるので、
そのため、@Stateをすることで更新されます。
1つ目は=での指定です。これに@Stateをつけると、エラーはなく問題なく処理ができます。値の更新(set)を行うこともできます。
そして1以外のもの、
2つめ、3つめ、4つめは、{}での指定です。
この{}を使うことで、その中に処理を書くことができます。

ということは、
結局@Stateではpublic var aaa;String = 100のように=を使った定義
@propertywrapperは、var aaa:string{}のような定義
で指定しないといけない

 

-IT, アプリ開発
-,

© 2024 Yosshi Labo. Powered by AFFINGER5