首先,ViewModifier是一个协议类型。
我们先来看一下官方例子:
struct BorderedCaption: ViewModifier {
func body(content: Content) -> some View {
content
.font(.caption2)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 1)
)
.foregroundColor(Color.blue)
}
}
这里我们写了一个结构体BorderedCaption 遵循 ViewModifier协议。方法体返回了一个view,这个view有一些我们prefer的格式。其实ViewModifier本质上就是这个function,它也只有这一个body方法,接收Content,然后返回一个view。
可这个Content又是什么东西呢?
如果我们跳到它的Definition ,我们可以看到:
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol ViewModifier {
/// The type of view representing the body.
associatedtype Body : View
/// Gets the current body of the caller.
///
/// `content` is a proxy for the view that will have the modifier
/// represented by `Self` applied to it.
func body(content: Self.Content) -> Self.Body
/// The content view type passed to `body()`.
typealias Content
}
它是一个typealias。可是还是没有解决我们的答案,这是什么东西?
Xcode编程为如果符号以下划线(_)开头,则不会显示SDK中的公共符号。 但是,通过在.swiftinterface文件中查找SwiftUI,我们可以看到ViewModifier的真实定义,包括隐藏的符号。
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol ViewModifier {
static func _makeView(modifier: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs) -> SwiftUI._ViewOutputs
static func _makeViewList(modifier: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewListInputs, body: @escaping (SwiftUI._Graph, SwiftUI._ViewListInputs) -> SwiftUI._ViewListOutputs) -> SwiftUI._ViewListOutputs
associatedtype Body : SwiftUI.View
func body(content: Self.Content) -> Self.Body
typealias Content = SwiftUI._ViewModifier_Content<Self>
}
我们可以看到Content是_ViewModifier_Content 的别名,该结构没有定义任何有趣的公共接口,但是(在扩展名中)符合View。 因此这告诉我们,当我们编写自己的ViewModifier时,我们的body方法将接收某种View(特定类型由框架定义,我们可以将其称为Content),并返回某种View(我们可以选择特定的返回类型)。
然后呢,我们通常给View在写一个扩展。
extension View {
func borderedCaption() -> some View {
modifier(BorderedCaption())
}
}
这里我们写了一个方法borderedCaption,返回值modifier接收一个遵循ViewModifier的实例化对象BorderedCaption()
然后我们就可以用了:
Image(systemName: "bus")
.resizable()
.frame(width:50, height:50)
Text("Downtown Bus")
.borderedCaption()
我们可以看到,Text可以直接调用我们自己封装的ViewModifer,改变样式。
当然,如果你不想扩展View. 你也可以这样用:
Image(systemName: "bus")
.resizable()
.frame(width:50, height:50)
Text("Downtown Bus")
.modifier(BorderedCaption())