美文网首页iOS Tips
SwiftUI — Data Flow Through Swif

SwiftUI — Data Flow Through Swif

作者: 丿唯一的唯一丿 | 来源:发表于2020-02-22 23:49 被阅读0次

    这一次我们来介绍一下 SwiftUI 里的属性装饰器@State, @Binding,@EnvironmentObject

    @States

    @frozen @propertyWrapper public struct State<Value> : DynamicProperty {
    
        /// Initialize with the provided initial value.
        public init(wrappedValue value: Value)
    
        /// Initialize with the provided initial value.
        public init(initialValue value: Value)
    
        /// The current state value.
        public var wrappedValue: Value { get nonmutating set }
    
        /// Produces the binding referencing this state value
        public var projectedValue: Binding<Value> { get }
    }
    

    @propertyWrapper 标注和之前提到的 的 @_functionBuilder类似 例如下面@State修饰的代码等同于 var showDetail = State(initialValue: false),会把使用过@State修饰器的属性存储到self上,但是这个属性和 View struct 是隔离的. 当@State装饰过的属性发生了变化,SwiftUI 会根据新的属性值更新视图

    struct ContentView: View {
        @State  var showFavoritesOnly: Bool = false
        //var showDetail = State(initialValue: false)
        var body: some View {
            VStack{
              Button(action: {
                    self.showFavoritesOnly.toggle()
                }) {
                    Text("Change")
                }
                if showFavoritesOnly {
                    Text("showFavoritesOnly")
                }else{
                    Text("showAll)
                }
            }
        }
    }
    

    @Binding

    上面例子是showDetail这个属性 button 不会自动修改,但有些控件是可以直接修改,就可以把一个视图的属性传至控件中,例如下面DetailView里面的Toggle就可以,但是又不能直接的传递给控件,因为在 Swift 中值的传递形式是值类型传递方式,也就是传递出去的是一个拷贝过的值。但是通过 @Binding 修饰器修饰后,用$也等同于@Binding,属性变成了一个引用类型,传递变成了引用传递,这样父子视图的状态就能关联起来了。

    struct ContentView: View {
        @State  var isFavorite: Bool
        var body: some View {
            VStack{
              /// The binding value, as "unwrapped" by accessing `$foo` on a `@Binding` property.
               DetailView(isFavorite: $isFavorite)
                if isFavorite {
                    Text("isFavorite")
                }else{
                    Text("NoFavorite")
                }
            }
        }
    }
    
    struct DetailView: View {
        @Binding var isFavorite: Bool
        var body: some View {
            Toggle(isOn: $isFavorite) {
                Text("Change Favorite")
            }
        }
    }
    

    在 DetailView 视图里用 @Binding 修饰 isFavorite 属性, 在传递属性是使用 $ 来传递 isFavorite 属性的引用,这样 DetailView 视图就能读写父视图 ContentView 里的状态值了,并且值发生了修改 SwiftUI 会更新 ContentView 和 DetailView 视图

    @EnvironmentObject

    从名字上可以看出,这个修饰器是针对全局环境的。通过它,我们可以避免在初始 View 时创建 ObservableObject, 而是从环境中获取 ObservableObject

    @Environment

    SwiftUI 本身就有很多系统默认设定,我们可以使用@Environment 来获取到它们

    @Environment(.editMode) var mode
    @Environment(.calendar) var calendar: Calendar
    @Environment(.locale) var locale: Locale
    @Environment(.colorScheme) var colorScheme: ColorScheme

    使用系统的还可以得到很多好处,例如你使用EditMode 就可以利用下面这些属性来完成一个编辑页面

    public enum EditMode {
    
        /// The view content cannot be edited.
        case inactive
    
        /// The view is in a temporary edit mode.
        ///
        /// The definition of temporary might vary by platform or specific control.
        /// As an example, temporary edit mode may be engaged over the duration of
        /// a swipe gesture.
        case transient
    
        /// The view content can be edited.
        case active
    
        /// Indicates whether a view is being edited.
        public var isEditing: Bool { get }
    
        /// Returns a Boolean value indicating whether two values are equal.
        ///
        /// Equality is the inverse of inequality. For any values `a` and `b`,
        /// `a == b` implies that `a != b` is `false`.
        ///
        /// - Parameters:
        ///   - lhs: A value to compare.
        ///   - rhs: Another value to compare.
    
    }
    

    你可以监听各种状态

    import SwiftUI
    
    struct ProfileHost: View {
    
        @Environment(\.editMode) var mode
    
        @State var draftProfile = Profile.default
    
        @EnvironmentObject var userData: UserData
    
        var body: some View {
              VStack(alignment: .leading, spacing: 20) {
                    HStack {
                        if self.mode?.wrappedValue == .active {
                        Button("Cancel") {
                            self.draftProfile = self.userData.profile
                            self.mode?.animation().wrappedValue = .inactive
                            }
                        }
                        Spacer()
                        EditButton()
                    }
                //
                if self.mode?.wrappedValue == .inactive {
                    ProfileSummary(profile: draftProfile)
                } else {
                    ProfileEditor(profile: $draftProfile)
                    .onAppear {
                    self.draftProfile = self.userData.profile
                    }
                    .onDisappear {
                    self.userData.profile = self.draftProfile
                 }
                }
            }
                  .padding()
        }
    }
    
    struct ProfileHost_Previews: PreviewProvider {
        static var previews: some View {
            ProfileHost()
        }
    }
    

    上述代码来自苹果教程 Working with UI Controls

    相关文章

      网友评论

        本文标题:SwiftUI — Data Flow Through Swif

        本文链接:https://www.haomeiwen.com/subject/xrlfqhtx.html