SwiftUI学习-(第3节)

2023-06-12  本文已影响0人  BoxJing
1.复杂View的代码块化

在写代码过程中我们经常会喜欢把一些代码单独放在一个方法里,方便调用,在SwiftUI中,也可以这么做,比如在前面例子中我们显示项的View,如果是一个复杂的展示,我们就可以单独放在一个showView供外部使用:

extension ContentView {
    @ViewBuilder var showView:some View {
        if (selectedItem != nil) {
            Text(selectedItem ?? "")
                .font(.largeTitle)
                .foregroundColor(.pink)
        }
    }
}

这样后我们直接可以在VStack中调用showView就行了,这里有个@ViewBuilder,会帮我们处理一些内部的实现,会发现直接在ContentView里面写的时候,没见过这个词,可以进到一个VStack的源码里:

@frozen public struct VStack<Content> : View where Content : View {

    /// Creates an instance with the given spacing and horizontal alignment.
    ///
    /// - Parameters:
    ///   - alignment: The guide for aligning the subviews in this stack. This
    ///     guide has the same vertical screen coordinate for every subview.
    ///   - spacing: The distance between adjacent subviews, or `nil` if you
    ///     want the stack to choose a default distance for each pair of
    ///     subviews.
    ///   - content: A view builder that creates the content of this stack.
    @inlinable public init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, @ViewBuilder content: () -> Content)

    /// The type of view representing the body of this view.
    ///
    /// When you create a custom view, Swift infers this type from your
    /// implementation of the required ``View/body-swift.property`` property.
    public typealias Body = Never
}

可以看到在初始化方法里有@ViewBuilder,系统内部已经帮我们实现了很多,当遇到一个if语句时候,如果我们没有写else的返回体时候,其实内部会帮我们实现一个EmptyView,可以直接看下@ViewBuilder的源码:

@resultBuilder public struct ViewBuilder {

    /// Builds an empty view from a block containing no statements.
    public static func buildBlock() -> EmptyView

    /// Passes a single view written as a child view through unmodified.
    ///
    /// An example of a single view written as a child view is
    /// `{ Text("Hello") }`.
    public static func buildBlock<Content>(_ content: Content) -> Content where Content : View
}

这里有个注意点,就是ViewBuilder里面最多只能放10个子View,如果有太多的话,可以用其他View包一下再放进去,比如Group

2.扩展

我们在oc和Swift中都可以针对某种控件进行扩展,在SwiftUI中同样适用,比如我们在前面的源码:

Button("随机一个") {
      print("选择了")
      selectedItem = arr.shuffled().filter{$0 != selectedItem}.first
}
.font(.title)
.buttonStyle(.borderedProminent)
.clipShape(RoundedRectangle(cornerRadius: 20.0, style: .circular))

而且这种样式在应用中反复会用到,那我们就可以进行一个封装成扩展:

extension View {
    func btnStyle() -> some View {
        font(.title)
        .buttonStyle(.borderedProminent)
        .clipShape(RoundedRectangle(cornerRadius: 20.0, style: .circular))
    }
}

这样封装后,我们前面的代码就可以简化为:

Button("随机一个") {
    print("选择了")
    selectedItem = arr.shuffled().filter{$0 != selectedItem}.first
}
.btnStyle()
3.Property Wrapper

在开发中我们可能经常会用到对象的属性是Int或者Float类型的,但是我们展示需要用String类型的,大部分都是直接在View中进行格式化。这是不太合理的,这种计算应该是对象内部自己处理的东西,比如一个对象:

struct Box: Equatable {
  var name: String
  var long: Double
  var width: Double
  var height: Double
}

我们展示盒子的长宽高的时候都希望后面加上cm,那我们可以再单独建一个显示的属性:

struct Box: Equatable {
  var name: String
  var long: Double
  var width: Double
  var height: Double
  var longString: String {long.formatted() + " cm"}
}

这样看起来还是有点繁琐,毕竟又多加了属性,而且调用时候是另一个属性了,那可以用Property Wrapper更简单的来处理:

@propertyWrapper struct Suffix: Equatable {
    var wrappedValue: Double
    private let suffix: String
    
    init(wrappedValue: Double, _ suffix: String) {
        self.wrappedValue = wrappedValue
        self.suffix = suffix
    }
    var projectValue: String {
        wrappedValue.formatted() + " \(suffix)"
    }
}

struct Box: Equatable {
    var name: String
    @Suffix("cm") var long: Double = .zero
    @Suffix("cm") var width: Double = .zero
    @Suffix("cm") var height: Double = .zero
}

在View中直接用 .$long.$width.$height调用就会显示 20 cm这样了。

上一篇 下一篇

猜你喜欢

热点阅读