I'm trying to scale a volumetric window's content when the user resizes the window. When I use modifier aspectRatio(contentMode: .fill)
, the content does not resize. Using scaledToFit()
doesn't scale, and it inexplicably distorts both the window and the content.
How do I scale the content of a volumetric window when the window is resized?
import RealityKit
import SwiftUI
@main
struct SandboxApp: App {
var body: some SwiftUI.Scene {
WindowGroup {
ContentView()
}
.windowStyle(.volumetric)
.defaultSize(width: 1, height: 1, depth: 1, in: .meters)
}
}
struct ContentView: View {
var body: some View {
RealityView { content in
content.add(ModelEntity(mesh: MeshResource.generateSphere(radius: 0.25),
materials: [SimpleMaterial(color: .red, isMetallic: false)]))
}
// .aspectRatio(contentMode: .fill) // does not resize
// .scaledToFill() // does not resize AND distorts volume and content
}
}
You can do it if you hang on to the Entity
and use setScale
based on the GeometryProxy3D
from GeometryReader3D
.
import SwiftUI
import RealityKit
struct BasicPhotoView<E>: View where E : Entity & HasModel {
var model: E
var body: some View {
GeometryReader3D { proxy in
RealityView { content in
content.add(model)
} update: { content in
let size = content.convert(proxy.frame(in: .local), from: .local, to: .scene)
model.resize(box: size)
}
}
}
}
extension Entity : HasModel {
public func resize(box: BoundingBox, style: Style = .aspectRatio) {
print(#function)
guard let meshBounds = self.model?.mesh.bounds.extents else {
print("no model")
return
}
// Convert extents to positive size vectors
let spaceSize = simd_float3(Float(box.extents.x), Float(box.extents.y), Float(box.extents.z))
let meshSize = simd_float3(meshBounds.x, meshBounds.y, meshBounds.z)
// Calculate scale factors for each dimension
let scaleFactor = spaceSize / meshSize
switch style {
case .fill:
// Use the exact scale factor for each dimension (non-uniform scaling)
self.setScale(abs(scaleFactor), relativeTo: nil)
case .aspectRatio:
// Use the minimum scale factor for uniform scaling
let uniformScale = min(scaleFactor.x, scaleFactor.y, scaleFactor.z)
let aspectRatioScale = simd_float3(repeating: uniformScale)
self.setScale(abs(aspectRatioScale), relativeTo: nil)
}
}
}