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 的初始化办法包含了几种功能的组合,包含:

  1. 内容类型约束;
  2. 身份辨认;
  3. 数据传递;
  4. 数据传递供给默认值。

内容类型约束

ImmersiveSpace 的 init 办法经过 init 添加泛型的方式,供给了针对内容的类型约束:

  1. 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。

  1. 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。

  1. 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 () }
    }
}
  1. init<V(id: String, content: () -> V) 经过 id 添加额定的内容类型约束

与内容类型约束声明相仿,不翻开阐明。

数据传递

在内容类型约束、身份辨认才能基础之上添加传递数据的才能,所以这儿会有四种类型:

  1. 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
  1. init<V(for: Data.Type, content: (Binding<Data?>) -> V)

同类型 1。

  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 对应的空间。

  1. init<V(id: String, for: Data.Type, content: (Binding<Data?>) -> V)

同类型 3。

数据传递默认值

这一部分的办法相较于数据传递部分只是添加了默认值才能的拓宽。

  1. 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)
  1. init<V(for: Data.Type, content: (Binding<Data) -> V, defaultValue: () -> Data)
  2. init<V(id: String, for: Data.Type, content: (Binding<Data) -> V, defaultValue: () -> Data)
  3. 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 匹配的款式之间切换。在后一种状况下,假如依据装备,透视作用会被遮挡,除了用户的手。