• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • @states ,@Binding,ObservableObject,@ObservedObject,@EnvironmentObject 分別是什么意思?SwiftUI數據傳遞

    標簽: Xcode  ios  swift

    您可以在github上查看:https://github.com/qizhemotuosangeyan/blog

    @states

    @Binding

    ObservableObject,@ObservedObject

    @EnvironmentObject

    分別是是什么

    • 急性子可以直接跳到最后看總結——不過我相信你會回來從頭再看的: )

    前置任務:屬性包裝器(propertyWrapper)

    上面的幾個標識符統統以@開頭,這表示他們均由@propertyWrapper產生,所以若要搞清楚這些東西分別有什么用需要先了解propertyWrapper

    • 使用@propertyWrapper產生一個屬性包裝器:@XXX,使用這個@XXX 對屬性進行包裝

    • 包裝前我想向你介紹幾個創建包裝器的時需要實現的屬性:wrappedValue(被包裝的值), projectedValue(呈現值)

    • 包裝器大概這樣產生

      @propertyWrapper struct 包裝器名稱 {
        var wrappedValue: 類型 {
          get {}
          set {}
        }
        var projectedValue: 類型 {
          return 
        }
        初始化方法(可選)
      }
      
    • 其中wrappedValue就是需要包裝的那個值,直接訪問,projectedValue就是你可能希望通過包裝額外呈現出來的某個值,$+屬性名稱訪問(下面有例子)

    //我想創造一個體溫溫度包裝器,使用該包裝器包裝的變量總是小于46.5且大于35(假定溫度計的極限是35-46.5度),同時呈現K氏度以供參考
    @propertyWrapper struct Temperature {
        var celcius = 35.0//攝氏溫度
        var wrappedValue: Double {
        get {
          return celcius
        }
        set {
          celcius = newValue <= 35 ? 35 : min(46.5, newValue)//如果小于等于35就顯示35度,否則在46.5和設置的溫度中取小
        }
        }
        var projectedValue: Double {
          return celcius + 273.15//K氏度= 攝氏度+ 273.15
        }
    }
    struct People {
        @Temperature(celcius: 36.5) var templete
    }
    let people = People()
    print("體溫:",people.templete,"K氏度:",people.$templete)
    
    

    運行結果[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳在這里插入圖片描述

    @states

    這個只要記住一句話:被@State包裝的值發生改變時,UI將被同時更變

    個人猜測底層在set方法中對UI進行了更新

    import SwiftUI
    //又一個字符串HelloWorld,按下按鈕后修改了被State包裝的變量x,所以UI也跟著發生了變化
    struct TestView: View {
        @State var x = "HelloWorld"
           var body: some View {
            VStack{
                Text(x)
                Button(action:{self.x = "Hello UI"}){
                    Text("ChangeValue")
                }
            }
           }
    }
    
    struct TestView_Previews: PreviewProvider {
        static var previews: some View {
            TestView()
        }
    }
    

    在這里插入圖片描述

    @Binding

    對包裝的值采用傳址而不是傳值(這里有C語言基礎的同學應該可以理解:也就是說傳遞了變量本身而不是拷貝了數據過去)

    import SwiftUI
    //例子:Text1和Text2是兩個不相關的View,他們共同使用一個數據,當該數據發生改變的時候除了要用@State告訴Text1更新UI,還要告訴Text2更新數字
    //button左邊的數字是Text1,右邊的數字是Text2
    struct Text1: View {
        @State var x = 5
        var body: some View {
            HStack{
                Text(String(x))
                Button(action: {self.x = 10}){
                    Text("change to 10")
                }
                Text2(y: $x)
            }
            
        }
    }
    struct Text2: View {
        @Binding var y: Int
        var body: some View {
            Text(String(y))
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            Text1()
        }
    }
    
    

    在這里插入圖片描述

    @ObservedObject——被觀察的對象

    ObservableObject——可以觀察(別人)的對象

    注意?:ObservableObject不是屬性包裝器,而是一個協議,滿足協議的類(Class)中使用@Publish標記可能發生改變的屬性。

    • 用@Publish標記類(Class)中希望監聽的屬性

    • 用@ObservedObject包裝該類(Class)的實例,當這個實例的某些被@Publish標記的屬性發生改變,(Publisher發布者)會在該實例被改變之前廣播給UI(數據的訂閱者)

    • 默認情況下,ObservableObject會合成一個objectWillChange發布者。

    import SwiftUI
    //例子:代碼解讀在代碼后面
    struct ContentView: View {
    @ObservedObject var john = Human(name: "john", age: 24)
        var body: some View {
            VStack {
                Text(john.name)
                Button(action:{
                    self.john.happyBirthday()
                }){
                    Text(String(john.age))
                }
            }
        }
    }
    
    class Human: ObservableObject {
        @Published var name: String
        @Published var age: Int
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    
        func happyBirthday() {
            self.name = "Happy birthday"
            self.age += 1
        }
        
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    //一個Human類滿足ObservableObject協議表示這個類在監聽某些屬性
    //@Publish name,@Publish age 表示這些屬性在被監聽
    //@ObservedObject var john 表示john對象是被監聽的對象
    //點擊按鈕調用self.john.happyBirthday(),改變了john的名字和年齡
    //UI收到了Publisher的廣播,自動更新了界面
    

    在這里插入圖片描述

    • 想比@State解決了哪些問題呢:這里借用喵神(https://onevcat.com)的一句話:

      如果你需要在多個 View 中共享數據,@State可能不是很好的選擇;如果還需要在 View 外部操作數據,那么 @State 甚至就不是可選項了。含有少數幾個成員變量的值類型,也許使用 @State 也還不錯。但是對于更復雜的情況,例如含有很多屬性和方法的類型,可能其 中只有很少幾個屬性需要觸發 UI 更新,也可能各個屬性之間彼此有關聯,那 么我們應該選擇引用類型和更靈活的可自定義方式。
      

    @EnvironmentObject

    文檔:A property wrapper type for an observable object supplied by a parent or ancestor view.

    翻譯:父視圖或祖先視圖提供的可觀察對象的屬性包裝器類型。

    注意?:強調可觀察(Class應當滿足ObservableObject協議),強調對象(必須是Class而不是Struct or Enum)

    
    import SwiftUI
    //代碼解釋在下面
    struct ContentView: View {
    @ObservedObject var john = Human(name: "john", age: 24)
        var body: some View {
            VStack {
                Text(john.name)
                Button(action:{
                    self.john.happyBirthday()
                }){
                    SubView()
                    .environmentObject(john)
                }
            }
        }
    }
    struct SubView: View {
        @EnvironmentObject var john: Human
        var body: some View {
            Text(john.name)
        }
    }
    class Human: ObservableObject {
        @Published var name: String
        @Published var age: Int
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    
        func happyBirthday() {
            self.name = "Happy birthday"
            self.age += 1
        }
        
    }
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    //在父View中的被觀察對象(被@ObservedObject包裝的john)在子View中使用,只需要在子View中用@EnviromentObject 包裝同類型的變量并在調用子View的時候使用.environmentObject(john)顯式的說明
    

    在這里插入圖片描述

    總結

    1. View與View間的公用數據使用@State + @Binding。

    2. 多個View與Class間的公用數據:對View用@ObservedObject,讓Class滿足ObservableObject協議。

    3. 父View與子View對Class間的公用數據:父View用@ObservedObject,子View用@EnvironmentObject,Class滿足ObservableObject協議

    版權聲明:本文為qq_42568693原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_42568693/article/details/105520318

    智能推薦

    SwiftUI macOS 之基于ObservableObject實現多Window窗口傳遞數據(教程含源碼)

    實戰需求 SwiftUI macOS 之基于ObservableObject實現多Window窗口傳遞數據 本文價值與收獲 看完本文后,您將能夠作出下面的界面 看完本文您將掌握的技能 管理一組TextField 使用ScrollView 設置圓形TextField 基礎知識 NSHostingController 托管SwiftUI視圖層次結構的AppKit視圖控制器。 總覽 當您要將SwiftU...

    【LeetCode】802. Find Eventual Safe States 解題報告(Python)

    【LeetCode】802. Find Eventual Safe States 解題報告(Python) 作者: 負雪明燭 id: fuxuemingzhu 個人博客: http://fuxuemingzhu.cn/ 題目地址:https://leetcode.com/problems/find-eventual-safe-states/description/ 題目描述: In a direc...

    [Leetcode] 802. Find Eventual Safe States 解題報告

    題目: 代碼: In a directed graph, we start at some node and every turn, walk along a directed edge of the graph.  If we reach a node that is terminal (that is, it has no outgoing directed edges), we s...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    猜你喜歡

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    精品国产乱码久久久久久蜜桃不卡