ImmersiveSpace 是用来在无限空间中出现其内容的场景。
该结构体的界说:
struct ImmersiveSpace<Content, Data> where Content : ImmersiveSpaceContent, Data : Decodable, Data : Encodable, Data : Hashable
运用 Immersive Space 作为运用出现的视图层次结构的容器。 Immersive Space 的内容的层次结构的示例:
@main
struct SolarSystemApp: App {
var body: some Scene {
ImmersiveSpace {
SolarSystem()
}
}
}
ImmersiveSpace 具有以下特性:
- 当你的运用程序翻开沉溺式空间时,体系会躲藏所有其他可见的运用程序;
- SwiftUI一次只答应翻开一个沉溺式空间。在翻开另一个沉溺式空间之前,封闭任何当前翻开的沉溺式空间;
- 你能够运用沉溺式空间作为运用程序的主界面。关于有边界的场景,请运用 WindowGroup;关于依据文档的运用程序,请运用 DocumentGroup;
- 你能够挑选界说一个 ImmersiveSpace 来出现契合 Hashable 和 Codable 类型的数据;
- 假如你传递一个数据给 openImmersiveSpace 操作以翻开沉溺式空间,那么该空间将把这个数据作为这个空间的根视图的绑定。体系会耐久保存这个绑定的值以便用于状态康复。康复空间时,体系会解码这个值并用于设定绑定。假如解码失利,体系会将绑定值设定为默认值或空。
创立 ImmersiveSpace
ImmersiveSpace 的初始化办法包含了几种功能的组合,包含:
- 内容类型约束;
- 身份辨认;
- 数据传递;
- 数据传递供给默认值。
内容类型约束
ImmersiveSpace 的 init 办法经过 init 添加泛型的方式,供给了针对内容的类型约束:
-
init(content: () -> Content)
无类型约束
界说:
public init(@ImmersiveSpaceContentBuilder content: @escaping () -> Content) where Data == NoImmersiveSpaceData
因为这个结构办法不需求供给数据的才能所以这儿 Data 类型为 NoImmersiveSpaceData,表示没有空间数据。
示例代码:
@main
struct SolarSystemApp: App {
var body: some Scene {
ImmersiveSpace {
ContentView()
}
}
}
留意:要求运用将这个 ImmersiveSpace 作为运用发动的首个 Scene 。
openImmersiveSpace 传递 “” 报错:No Immersive Space with id ” is defined。
-
init<V>(content: () -> V)
约束内容类型
@main
struct SolarSystemApp: App {
var body: some Scene {
ImmersiveSpace < ImmersiveSpaceViewContent < LimitedClassView >, NoImmersiveSpaceData >() { LimitedClassView . init () }
}
}
开发者无需完成 ImmersiveSpaceViewContent (但 API 依然对外可用),能够经过 init 办法指定泛型:
@main
struct SolarSystemApp: App {
var body: some Scene {
ImmersiveSpace . init < LauncherWithLimitedView > () { LauncherWithLimitedView . init () }
}
}
留意:要求运用将这个 ImmersiveSpace 作为运用发动的首个 Scene 。
openImmersiveSpace 传递 “” 报错:No Immersive Space with id ” is defined。
这一类带有类型约束才能的办法经过拓宽来出现,包含处理数据传递和类型辨认的组合:
@available(visionOS 1.0, *)
@available(iOS, unavailable)
@available(macOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
extension ImmersiveSpace {
public init<V>(@ViewBuilder content: @escaping () -> V) where Content == ImmersiveSpaceViewContent<V>, Data == NoImmersiveSpaceData, V : View
public init<V>(id: String, @ViewBuilder content: () -> V) where Content == ImmersiveSpaceViewContent<V>, Data == NoImmersiveSpaceData, V : View
public init<V>(id: String, for type: Data.Type, @ViewBuilder content: @escaping (Binding<Data?>) -> V) where Content == ImmersiveSpaceViewContent<V>, V : View
public init<V>(for type: Data.Type, @ViewBuilder content: @escaping (Binding<Data?>) -> V) where Content == ImmersiveSpaceViewContent<V>, V : View
public init<V>(id: String, for type: Data.Type = Data.self, @ViewBuilder content: @escaping (Binding<Data>) -> V, defaultValue: @escaping () -> Data) where Content == ImmersiveSpaceViewContent<V>, V : View
public init<V>(for type: Data.Type = Data.self, @ViewBuilder content: @escaping (Binding<Data>) -> V, defaultValue: @escaping () -> Data) where Content == ImmersiveSpaceViewContent<V>, V : View
}
泛型类型的办法后续不再进行阐明,能够参考这儿查询对应内容。
身份辨认
经过添加 id 参数,便于开发者创立 ImmersiveSpace 时,供给一个仅有辨认符,后续能够将仅有辨认符传递给 openImmersiveSpace ,来翻开指定的 Space。
-
init(id: String, content: () -> Content)
经过 id 参数进行空间辨认
办法的界说:
public init(id: String, @ImmersiveSpaceContentBuilder content: () -> Content) where Data == NoImmersiveSpaceData
用法示例:
@main
struct SolarSystemApp: App {
var body: some Scene {
ImmersiveSpace (id: "ImmersiveSpace" ) { ImmersiveView () }
}
}
-
init<V(id: String, content: () -> V)
经过 id 添加额定的内容类型约束
与内容类型约束声明相仿,不翻开阐明。
数据传递
在内容类型约束、身份辨认才能基础之上添加传递数据的才能,所以这儿会有四种类型:
-
init(for: Data.Type, content: (Binding<Data?>) -> Content)
无类型约束,无身份辨认,供给数据传递才能
界说:
///
/// - Parameters:
/// - type: 该空间接受的出现数据的类型。
/// - content: 一个沉溺式空间内容构建器,用于界说空间的内容。
/// 闭包接纳经过 ``EnvironmentValues/openImmersiveSpace`` 传递的值的绑定。
/// 此绑定的值将在空间的状态康复期间进行耐久保存和康复。
public init(for type: Data.Type, @ImmersiveSpaceContentBuilder content: @escaping (Binding<Data?>) -> Content)
当你运用 EnvironmentValues/openImmersiveSpace
操作出现指定 type
的值时,会调用此办法。
参数阐明:
- type: 该空间接受的出现数据的类型。
- content: 一个沉溺式空间内容构建器,用于界说空间的内容。
运用方式:
// 1. 声明 Space
@main
struct SolarSystemApp: App {
var body: some Scene {
ImmersiveSpace . init (for: String . self ) { binding in PresentParamDataView (data: binding.wrappedValue ?? "default" ) }
}
}
// 2. 翻开该 Space
func openSpaceWithData ( data : String ) { Task { await openImmersiveSpace(value: data) } }
openSpaceWithData 办法将 data 数据,经过 openImmersiveSpace 传递给了对应声明的空间。
这种方式没有身份辨认,但是能够经过传递的数据类型进行空间匹配。
假如同时装备两个接纳相同类型的 Space,则体系会同时翻开,翻开空间的顺序遵循 App 中装备 Space 的顺序,但因为一次只能翻开一个空间,所以成功翻开第一个,后边的都无法翻开,并报错:
Unable to present another Immersive Space when one is already requested or connected
init<V(for: Data.Type, content: (Binding<Data?>) -> V)
同类型 1。
init(id: String, for: Data.Type, content: (Binding<Data?>) -> Content)
public init(id: String, for type: Data.Type, @ImmersiveSpaceContentBuilder content: @escaping (Binding<Data?>) -> Content)
相较于类型 1,添加了 id 参数,也便是身份验证的才能。
参数阐明:
- id: 用于仅有标识沉溺式空间的字符串。
- type: 该空间接受的出现数据的类型。
- content: 一个沉溺式空间内容构建器,用于为空间的每个实例创立内容。
因为添加了 id 作为空间仅有辨认符,所以运用 openImmersiveSpace 传递给了对应声明的空间。
用法示例:
// 1. 声明 Space
@main
struct SolarSystemApp: App {
var body: some Scene {
ImmersiveSpace . init (id: "Space1" , for: String . self ) { binding in PresentParamDataView (data: binding.wrappedValue ?? "default" ) }
}
}
// 2. 翻开该 Space
func openSpaceWithData ( data : String ) { Task { await openImmersiveSpace(id: "Space1" , value: data) } }
这样就能够翻开指定 id 对应的空间。
init<V(id: String, for: Data.Type, content: (Binding<Data?>) -> V)
同类型 3。
数据传递默认值
这一部分的办法相较于数据传递部分只是添加了默认值才能的拓宽。
init(for: Data.Type, content: (Binding<Data) -> Content, defaultValue: () -> Data)
界说:
public init(id: String, for type: Data.Type = Data.self, @ImmersiveSpaceContentBuilder content: @escaping (Binding<Data>) -> Content, defaultValue: @escaping () -> Data)
init<V(for: Data.Type, content: (Binding<Data) -> V, defaultValue: () -> Data)
init<V(id: String, for: Data.Type, content: (Binding<Data) -> V, defaultValue: () -> Data)
init(id: String, for: Data.Type, content: (Binding<Data) -> Content, defaultValue: () -> Data)
声明示例:
ImmersiveSpace . init (id: "Space2" , for: String . self ) { binding in ContentView (name: binding.wrappedValue) } defaultValue: { "A" }
需求留意的是这儿 defaultValue 经过 closure 的方式来供给;另外一点是相较于没有供给默认值的办法,binding 的
wrappedValue 变为非空,而不需求处理可能为 nil 的状况。
翻开 ImmersiveSpace
SwiftUI 的 EnvironmentValues 中供给了 openImmersiveSpace 用于展现沉溺式空间的操作的拓宽。
extension EnvironmentValues {
public var openImmersiveSpace: OpenImmersiveSpaceAction { get }
}
调用会在沉溺式空间出现后或发生错误时回来。该办法适用于 SwiftUI。
下面是个运用示例,展现了界说一个按钮,用于在新空间中翻开指定的太阳系空间:
@main
struct SolarSystemApp : App {
var body: some Scene {
ImmersiveSpace (for: SolarSystem . ID . self ) { $solarSystemID in
// ...
}
}
}
struct NewSolarSystemImmersiveSpace : View {
var solarSystem: SolarSystem
@Environment (.openImmersiveSpace) private var openImmersiveSpace
var body: some View {
Button ( "在新沉溺式空间中展现太阳系" ) {
Task {
await openImmersiveSpace(value: solarSystem. ID )
}
}
}
}
为了取得最佳性能,value 应运用轻量级数据。关于契合可辨认的结构化 Model 的值,值的标识符是一个很好的表示值。
openImmersiveSpace 特点类型是 OpenImmersiveSpaceAction,只是完成了 callAsFunction ,所以能够作为函数调用。
Swift 5.2的新功能是能够将类型的实例作为函数来调用。 或许,如Swift Evolution提案所称,它是“用户界说的标称类型的可调用值”。 此功能的简略描绘是,它答应您调用完成了callAsFunction办法的任何类型的实例,就好像它是一个函数相同。docs.swift.org/swift-book/…
public struct OpenImmersiveSpaceAction : Sendable {
@discardableResult
@MainActor public func callAsFunction(id: String) async -> OpenImmersiveSpaceAction.Result
@discardableResult
@MainActor public func callAsFunction<D>(value: D) async -> OpenImmersiveSpaceAction.Result where D : Decodable, D : Encodable, D : Hashable
@discardableResult
public func callAsFunction<D>(id: String, value: D) async -> OpenImmersiveSpaceAction.Result where D : Decodable, D : Encodable, D : Hashable
}
callAsFunction 供给了三种类型别离对应:
-
callAsFunction(id: String)
, 仅经过 id 翻开 Space -
callAsFunction<D>(value: D)
,仅经过数据翻开 Space -
callAsFunction<D>(id: String, value: D)
, 经过 id 翻开 Space,并传递数据。
需求留意的是这些调用都被规划成异步的操作。 回来类型是 OpenImmersiveSpaceAction.Result
:
public struct OpenImmersiveSpaceAction : Sendable {
/// Represents the result of opening an immersive space.
public enum Result : Sendable {
/// Opening the immersive space succeeded.
case opened
/// Opening the immersive space failed since the user cancelled the
/// request.
case userCancelled
/// Opening the immersive space failed since the system cannot fulfill
/// the request.
case error
public static func == (a: OpenImmersiveSpaceAction.Result, b: OpenImmersiveSpaceAction.Result) -> Bool
public func hash(into hasher: inout Hasher)
public var hashValue: Int { get }
}
}
枚举类型表示翻开空间的结果,有三种类型:
- 成功翻开沉溺式空间
- 因为用户取消,翻开沉溺式空间失利
- 因为体系无法满意请求,翻开沉溺式空间失利
封闭 ImmersiveSpace
SwiftUI 的 EnvironmentValues 中供给了 dismissImmersiveSpace 用于封闭沉溺式空间的操作的拓宽。
extension EnvironmentValues {
public var dismissImmersiveSpace: DismissImmersiveSpaceAction { get }
}
调用会在沉溺式空间封闭后回来。该办法适用于 SwiftUI。
用法示例:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "solarSystem") {
SolarSystemView()
}
}
}
struct DismissImmersiveSpaceButton: View {
@Environment(.dismissImmersiveSpace) private var dismissImmersiveSpace
var body: some View {
Button("封闭太阳系沉溺式空间") {
Task {
await dismissImmersiveSpace()
print("封闭完成")
}
}
}
}
dismissImmersiveSpace 的类型是 DismissImmersiveSpaceAction,也是经过 callAsFunction 供给的函数调用方式的才能:
public struct DismissImmersiveSpaceAction {
/// 封闭当前翻开的沉溺式空间。
/// 调用会在空间封闭后回来。
///
/// 不要直接调用此办法。SwiftUI 在你调用 ``EnvironmentValues/dismissImmersiveSpace`` 操作时会调用它:
/// await dismissImmersiveSpace()
@MainActor public func callAsFunction () async
}
留意这也是一个异步的办法。
空间类型声明
在声明 ImmersiveSpace 时,经过 immersionStyle 指定空间的类型:
ImmersiveSpace (id: "ImmersiveSpace" ) {
TestForParamSpace ()
}
.immersionStyle(selection: $immersionState , in: .progressive)
immersionStyle 的界说:
@available (visionOS 1.0 , * )
@available ( iOS , unavailable)
@available ( macOS , unavailable)
@available ( tvOS , unavailable)
@available ( watchOS , unavailable)
extension Scene {
/// Sets the allowed styles for the immersive space.
/// - Parameters:
/// - selection: A binding to the effective style used by the space.
/// - styles: The list of styles that the immersive space allows.
public func immersionStyle ( selection : Binding < ImmersionStyle >, in styles : ImmersionStyle ...) -> some Scene
}
作为 Scene 的拓宽的方式供给的,而不是 ImmersiveSpace 的拓宽 。意思是 WindowGroup 也能用?
它有两个参数:
- selection:绑定到空间运用的有效款式。
- styles:沉溺式空间答应的风格列表。
需求留意的是,selection 假如不在 styles 答应的范围内,默认会运用 styles 列表中的第一个 style 风格。
ImmersionStyle
ImmersionStyle 代表沉溺式空间的风格。
风格影响沉溺式空间的外观和行为。要装备沉溺式空间的当前风格,请运用 immersionStyle(selection:in:)
修饰符。在创立空间时指定契合 ImmersionStyle 的款式。
内置款式类型包含四种:
static var automatic: AutomaticImmersionStyle
默认的沉溺式风格。默认状况下,SwiftUI 将 ImmersionStyle/mixed
款式作为主动款式。
static var full: FullImmersionStyle
一种沉溺式款式,显现 pass-through ****视频的无边界内容。空间内容彻底遮挡了透视视频,除了用户的手装备为显现的状况下,手部能够穿透展现。
static var mixed: MixedImmersionStyle
一种沉溺式款式,显现与其他运用程序内容混合的无边界内容,并显现透视视频。
static var progressive: ProgressiveImmersionStyle
一种沉溺式款式,其间内容显现时没有运用剪裁边界。
体系最初运用 Portal 作用。随后,人们能够交互式地调整缩放,以在 Portal 款式和与FullImmersionStyle
匹配的款式之间切换。在后一种状况下,假如依据装备,透视作用会被遮挡,除了用户的手。