苹果的新SwiftUI
框架似乎使用了一种 新型语法 ,可以有效地构建元组,但又具有另一种语法:
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World") // No comma, no separator ?!
Text("Hello World!")
}
}
尝试解决这种语法的实际含义时 ,我发现VStack
这里使用的初始化程序将类型的闭包() -> Content
作为第二个参数,其中Content
的通用参数View
是通过闭包推断的。为了找出要Content
推断的类型,我对代码进行了一些更改,并保持其功能:
var body: some View {
let test = VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
}
return test
}
以此,test
表明自己是类型VStack<TupleView<(Text, Text)>>
,即Content
类型TupleView<Text, Text>
。向上看TupleView
,我发现它是一个源自SwiftUI
自身的包装器类型,只能通过传递应该包装的元组来进行初始化。
题
现在,我想知道Text
这个示例中的两个实例如何转换为TupleView<(Text, Text)>
。这被黑入SwiftUI
,因此
无效的常规Swift语法吗? TupleView
作为一种SwiftUI
类型支持这种假设。还是这个 有效的Swift语法?
如果是,请问如何 在室外使用它SwiftUI
?
正如马丁说,如果你看的文件VStack
的init(alignment:spacing:content:)
,你可以看到content:
参数具有的属性@ViewBuilder
:
init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
**@ViewBuilder** content: () -> Content)
此属性引用ViewBuilder
类型,如果您查看生成的接口,则该类型类似于:
**@_functionBuilder** public struct ViewBuilder {
/// Builds an empty view from an block containing no statements, `{ }`.
public static func buildBlock() -> EmptyView
/// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
/// through unmodified.
public static func buildBlock(_ content: Content) -> Content
where Content : View
}
该@_functionBuilder
属性是一个非官方功能的一部分,该功能称为“
函数构建器 ”,在此处已针对Swift演变推出,并专门针对Xcode 11附带的Swift版本实现,从而可以在SwiftUI中使用。
标记类型@_functionBuilder
允许将其用作各种声明(例如函数,计算属性以及在这种情况下为函数类型的参数)上的自定义属性。此类带注释的html" target="_blank">声明使用函数构建器来转换代码块:
函数构建器转换代码的方式由构建器方法(例如)的实现定义,该方法buildBlock
采用一组表达式并将其合并为单个值。
例如,ViewBuilder
实现buildBlock
1到10个View
符合参数的实现,将多个视图合并为一个视图TupleView
:
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension ViewBuilder {
/// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)
/// through unmodified.
public static func buildBlock<Content>(_ content: Content)
-> Content where Content : View
public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1)
-> TupleView<(C0, C1)> where C0 : View, C1 : View
public static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2)
-> TupleView<(C0, C1, C2)> where C0 : View, C1 : View, C2 : View
// ...
}
这允许传递给VStack
的初始化程序的闭包内的一组视图表达式被转换为对调用的调用,buildBlock
该调用采用相同数量的参数。例如:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
}
}
}
转换为对的调用buildBlock(_:_:)
:
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))
}
}
}
导致不透明的结果类型 someView
由满足TupleView<(Text, Text)>
。
您会注意到,最多ViewBuilder
只能定义buildBlock
10个参数,因此,如果我们尝试定义11个子视图,则:
var body: some View {
// error: Static member 'leading' cannot be used on instance of
// type 'HorizontalAlignment'
VStack(alignment: .leading) {
Text("Hello, World")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
Text("Hello World!")
}
}
我们会收到一个编译器错误,因为没有构建器方法可以处理此代码块(请注意,由于此功能仍在开发中,因此围绕它的错误消息不会有太大帮助)。
实际上,我不认为人们会经常遇到这种限制,例如,使用ForEach
视图代替上面的示例会更好:
var body: some View {
VStack(alignment: .leading) {
ForEach(0 ..< 20) { i in
Text("Hello world \(i)")
}
}
}
但是,如果确实需要超过10个静态定义的视图,则可以使用该Group
视图轻松解决此限制:
var body: some View {
VStack(alignment: .leading) {
Group {
Text("Hello world")
// ...
// up to 10 views
}
Group {
Text("Hello world")
// ...
// up to 10 more views
}
// ...
}
ViewBuilder
还实现其他功能构建器方法,例如:
extension ViewBuilder {
/// Provides support for "if" statements in multi-statement closures, producing
/// ConditionalContent for the "then" branch.
public static func buildEither<TrueContent, FalseContent>(first: TrueContent)
-> ConditionalContent<TrueContent, FalseContent>
where TrueContent : View, FalseContent : View
/// Provides support for "if-else" statements in multi-statement closures,
/// producing ConditionalContent for the "else" branch.
public static func buildEither<TrueContent, FalseContent>(second: FalseContent)
-> ConditionalContent<TrueContent, FalseContent>
where TrueContent : View, FalseContent : View
}
这使它能够处理if语句:
var body: some View {
VStack(alignment: .leading) {
if .random() {
Text("Hello World!")
} else {
Text("Goodbye World!")
}
Text("Something else")
}
}
变成:
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(
.random() ? ViewBuilder.buildEither(first: Text("Hello World!"))
: ViewBuilder.buildEither(second: Text("Goodbye World!")),
Text("Something else")
)
}
}
(发出冗余的1个参数需要ViewBuilder.buildBlock
明确说明)。
苹果新的框架似乎使用了一种新的语法,可以有效地构建元组,但有另一种语法: 试图解决这个语法到底是什么,我发现这里使用的初始化器需要类型
协议定义如下: 因此,现在是一个过时的协议,不能直接用作返回类型,尽管swift5.1的不透明返回类型可以处理这个问题,但是为什么要声明一个,而不是?
问题内容: 我刚刚用来为我的JPA2实体生成MetaModel。 有人可以解释为什么在这种情况下将属性标记为易失性吗? 谢谢。 问题答案: 设置静态变量的线程可能与用于访问它们的线程不同,因此需要使用修饰符在所有线程之间同步内存。 没有的情况是这样的: 在初始化JPA提供程序之前,您的线程将访问变量,并获取静态字段 JPA提供程序是从其他线程初始化的,并将静态字段设置为非空值 您的线程再次访问静态
问题内容: 我正在学习SwiftUI。我遇到了“ GeometryReader”。我想知道为什么以及何时使用它? 问题答案: 更新 自从发布答案以来,我还写了一篇有关GeometryReader如何工作的文章。 查看它以获取更详细的说明:https : //swiftui-lab.com/geometryreader-to-the-rescue/ GeometryReader是一个视图,使您可以访
当使用Java8类时,有两种方法可以将值包装到Optional中。 我理解是使用的唯一安全方法,但为什么?为什么不直接使用
为了区分(a)任何值、(b)显式null和(c)无值的情况,我使用和注释。 这在Spring Boot 2.1.0/Jackson 2.9.7中非常适用。 目前(更新后)似乎像一样处理。它呈现,尽管该字段是用注释的。在Spring Boot 2.5.5/Jackson 2.12.5中,它呈现: