Open12

@Bindingと@Bindableって何が違うの?

ぴょんさんぴょんさん

動きの違い

class/@Binding

class Book: Identifiable {
    var id = UUID()
    var title: String = ""
    var Read: Bool = false
}
import SwiftUI

struct ContentView: View {
    @State var book = Book()
      
    var body: some View {
        VStack {
            TextView(book: $book)
            Toggle(isOn: $book.Read, label: {
                Text("Read")
            })
        }
        .padding()
    }
}


struct TextView: View {
    @Binding var book: Book
    var body: some View {
    
        Text(String(book.Read))
    }
}

これだとtoggleをonoffしてもTextは変わらなかった!

ぴょんさんぴょんさん

class+ @Observable/@Binding

@Observable
class  Book: Identifiable {
    var id = UUID()
    var title: String = ""
    var Read: Bool = false
}

struct ContentView: View {
    @State var book = Book()
      
    var body: some View {
        VStack {
            TextView(book: $book)
            Toggle(isOn: $book.Read, label: {
                Text("Read")
            })
        }
        .padding()
    }
}


struct TextView: View {
    @Binding var book: Book
    var body: some View {
    
        Text(String(book.Read))
    }
}

これだとtoggleをonoffしたらTextは変わった!

ぴょんさんぴょんさん

class+ @Observable/@Bindable

@Observable
class  Book: Identifiable {
    var id = UUID()
    var title: String = ""
    var Read: Bool = false
}

struct ContentView: View {
    @State var book = Book()
      
    var body: some View {
        VStack {
            TextView(book: book)
            Toggle(isOn: $book.Read, label: {
                Text("Read")
            })
        }
        .padding()
    }
}


struct TextView: View {
    @Bindable var book: Book
    var body: some View {
    
        Text(String(book.Read))
    }
}

これだとtoggleをonoffしたらTextは変わった!

ぴょんさんぴょんさん

struct/@Binding

struct  Book: Identifiable {
    var id = UUID()
    var title: String = ""
    var Read: Bool = false
}
struct ContentView: View {
    @State var book = Book()
      
    var body: some View {
        VStack {
            TextView(book: $book)
            Toggle(isOn: $book.Read, label: {
                Text("Read")
            })
        }
        .padding()
    }
}


struct TextView: View {
    @Binding var book: Book
    var body: some View {
    
        Text(String(book.Read))
    }
}

これだとtoggleをonoffしたらTextは変わった!

ぴょんさんぴょんさん

@StateObject→@Stateに
@ObservedObject→@Bindableに
@EnvironmentObject→@Environment

ios17以前は値型(struct)には @Binding、参照型(class)には @ObservedObjectを使っていた

ぴょんさんぴょんさん

@StateObjectと@ObservedObjectの違いは?

そもそも@StateObjectと@ObservedObjectはproperty wrapperで、データの変更を監視して、値が変更されたら、Viewを自動的に更新してくれる。
ライフサイクルが違う

@ObservedObject

親Viewのbodyが再描画されると、@ObservedObjectの値はリセットされる

@StateObject

親Viewが表示されて非表示になるまでは@StateObjectの値はリセットされない
再描画されても値はリセットされない

ぴょんさんぴょんさん

これに全部があった!

Discover Observation in SwiftUI
日本語訳

ViewModelに@ObservableObjectをつける

struct ContentView: View {
    @ObservedObject var vm = viewModel()
      
    var body: some View {
        VStack {
            Text(String(vm.book.Read))
            Toggle(isOn: $vm.book.Read, label: {
                Text("Read")
            })
         
        }
        .padding()
    }
}

final class viewModel: ObservableObject {
    @Published var book = Book()
}

@Publishedがいる

ViewModelに@Observableをつける

struct ContentView: View {
    @Bindable var vm = viewModel()
      
    var body: some View {
        VStack {
            Text(String(vm.book.Read))
            Toggle(isOn: $vm.book.Read, label: {
                Text("Read")
            })
         
        }
        .padding()
    }
}

@Observable
final class viewModel {
 var book = Book()
}

書きやすくなった!

ぴょんさんぴょんさん

結局@bindingと@Bindableの違いって?

@BindableはObservableプロトコルに準拠したクラスでしか使えない

値が何かによってつけるものが変わる
@Bindable→Observableプロトコルに準拠したクラス
@Binding→構造体や準拠してないクラス

ObservedObjectラッパーが@Bindableに変わった

子ビューが参照全体を別のものに変更する必要がある場合は、@Bindingを使用。オブジェクトのプロパティへのバインディングのみが必要な場合は、@Bindableを使用する
@Observableクラスのプロパティは@Bindingをサポートしてるので@Bindingも使える

@Bindable var bookTitle: String できない
@Binding var bookTitle: String できる
そもそもStringは値型(struct)なので@Bindableは使えない

  struct ContentView: View {
  @Bindable var vm: viewModel = viewModel()
    var body: some View {
      VStack {
          // @Bindingを使用したビュー
         // var vm: viewModel = viewModel()だと$vm.book.titleで値渡しできない
          Children(book: $vm.book, bookTitle: $vm.book.title)
          
          // 通常のビュー
          Children2(book: vm.book)
          
          // @Bindableでも大丈夫
          Children3(book: vm.book)

          //値を変更する
          Child4(book: vm.book)
      }
  }
}

//Book型を@Bindingで持ってくる
struct children: View {
  @Binding var book: Book
  @Binding var bookTitle: String
  var body: some View {
      Text(book.title)
      Text(bookTitle)
  }
}

//Bookに@Observableがついて監視対象なので、@BindingなしでOK
struct children2: View {
var book: Book
  var body: some View {
      Text(book.title)
  }
}

//Book型を@Bindableで持ってくる
struct children3: View {
  var book: Book
  var body: some View {
      Text(String(book.Read))
  }
}
//bookの値を変える
struct children4: View {
  @Bindable var book: Book
  //bindingの値を書き換えるときは@bindableいる

  var body: some View {
      Toggle(isOn:$book.Read, label: {
          Text("Read")
      })
  }
}

@Observable
final class viewModel {
var book = Book()
}

@Observable
final class Book: Identifiable {
  var id = UUID()
  var title: String = "title"
  var Read: Bool = false
}

ぴょんさんぴょんさん

値を書き換えるときは@bindableがいるのはなぜ?

調べ中
プロパティの変更を追跡されてないから?
双方向のデータ更新?

よみよみ

Bindable = インスタンスごと書き換えられない。中身の変数だけ書き換えられる
Binding = インスタンスごと書き換え可能

struct People {
    name: String
    age: Int
}
@Bindable var people1: People
people1 = People() //×

@Binding var people1
people1 = People() //◯
@Bindable var people: People
people.name = "jason" //◯

@Binding var people: People
people.name = "jason" //◯
@Bindable var name: String 
name = "jason" //×
@Binding var name: String
name = "jason" //◯

struct CustomText() {
    @Binding people: People
    ...
}
@Bindable var people: People = People()
CustomText($people) //×