SwiftUI ViewModifier

2,743 阅读2分钟

首先,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())