美文网首页SwiftUISwiftUI
SwiftUI之属性包装

SwiftUI之属性包装

作者: 狂奔的胖蜗牛 | 来源:发表于2019-11-01 10:36 被阅读0次

https://github.com/flywo/SwiftUIPractice
自己写的SwiftUI练习项目,各位看官关注一下。


SwiftUI重度依赖于属性包装,使我们代码容易被阅读、书写和维护。

本文尽量从自己的角度解读一下SwiftUI属性包装的作用,先总结一下:

  • 1.一些包装能够使我们获得和修改数据,比如@State能让我们修改Struct的属性。
  • 2.一些包装要求你做一些特别的工作,假如没有做,程序会崩溃。
  • 3.同时对一个属性只能进行一次包装,比如@ObservedObject @Binding var value = SomeClass()是不允许。
  • 4.有些包装看起来是一样的,但是使用的场景却是根据环境变化的,比如@Environment和@EnvironmentObject。
  • 5.你可以自定义包装。不过一般不建议这么做。SwiftUI提供的已经够用了。

@State

@State属性允许你修改Struct的属性,这些属性在普通的Struct里面是不允许修改的。当把@State放置到属性前,该属性实际上会被放到Struct的外部存储起来,这意味着SwiftUI能够随时销毁和重建Struct而不会丢失属性的值。

@State包装的属性通常是设置成私有的,不让外部使用。如果想让外部使用,则应该使用@ObservedObject和@EnvironmentObject,他们能够使外部修改属性后,状态能够得到改变。

建议把@State包装的属性都设置成私有:

@State private var username = ""

@Published

@Published是SwiftUI最有用的包装之一,允许我们创建出能够被自动观察的对象属性,SwiftUI会自动监视这个属性,一旦发生了改变,会自动修改与该属性绑定的界面。

1.首先需要遵循ObservableObject属性

class Bag: ObservableObject {
    var items = [String]()
}

2.包装属性

class Bag: ObservableObject {
    @Published var items = [String]()
}

这样就完成了。@Published包装会自动添加willSet方法监视属性的改变。

@ObservedObject

@ObservedObject告诉SwiftUI,这个对象是可以被观察的,里面含有被@Published包装了的属性。

例子:

class Order: ObservableObject {
    @Published var items = [String]()
}

struct ContentView: View {
    @ObservedObject var order = Order()

    var body: some View {
    }
}

@ObservedObject包装的对象,必须遵循ObservableObject协议。也就是说必须是class对象,不能是struct。

@ObservedObject允许外部进行访问和修改。

@EnvironmentObject

@EnvironmentObject包装的属性是全局的,整个app都可以访问。

比如创建一个订单,所有页面都能访问和使用

class Order: ObservableObject {
    @Published var items = [String]()
}

struct ContentView: View {
    @EnvironmentObject var order: Order

    var body: some View {
    }
}

需要注意的是,不需要给定默认值,由于该属性是整个app都可以访问的,SwiftUI会自动的从环境中取出来。

可以想象一下,有这样一个场景,A->B->C->D->E->F,A界面的数据要传递给F界面,假如使用@ObservedObject包装,则需要一层一层传递,而使用@EnvironmentObject则不需要,直接在F界面,通过SwiftUI环境直接取出来就行。

需要注意的是,当界面显示时,就会去环境中取,但是,假如之前没有把属性放到环境中,则程序会崩溃。

@Environment

@Environment与@EnvironmentObject作用是不同的,@Environment是从环境中取出预定义的值,比如获得当前是暗黑模式还是正常模式,屏幕的大小等等。

@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.managedObjectContext) var managedObjectContext

@Binding

@Binding用的比较少,但是也是非常重要的一个包装,声明一个属性是从外部获取的,并且与外部是共享的。相当于外部传过来的时候,不是传递的值。

例子:

struct ContentView: View {
    @State private var showingAddUser = false

    var body: some View {
        VStack {
        }
    }
    .sheet(isPresented: $showingAddUser) {
        AddView(isPresented: self.$showingAddUser)
    }
}

struct AddView: View {
    @Binding var isPresented: Bool

    var body: some View {
        Button("Dismiss") {
            self.isPresented = false
        }
    }
}

我们用showingAddUser属性来决定sheet是否弹出。同时把该属性传递给AddView,在AddView内部,让sheet消失。

该属性让ContentView与AddView共享showingAddUser属性,任何一方修改都会让SwiftUI的监视生效。

@GestureState

@GestureState能够让我们传递手势的状态,虽然使用@State也能实现,但@GestureState能让手势结束后我们回到最初的状态。

1.设置属性,设置zero意味着手势结束后,会回到最初的值。

@GestureState var dragAmount = CGSize.zero

2.使用属性

Image("example-image")
    .offset(dragAmount)

3.添加手势

Image("example-image")
    .offset(dragAmount)
    .gesture(
        DragGesture().updating($dragAmount) { value, state, transaction in
            state = value.translation
        }
    )

分析一下:

  • 1.DragGesture().updating()创建出手势,传入dragAmount属性。
  • 2.闭包返回了三个值:value, state, transaction
  • 3.value参数包含了当前拖动的数据,开始点、移动位置等
  • 4.state参数是一个inout参数,因此我们需要修改他,实现将属性存储到dragAmount中。
    1. transaction参数也是一个inout参数,包含了动画的信息。

相关文章

网友评论

    本文标题:SwiftUI之属性包装

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