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
这样了。
网友评论