返回 方案一:修改
方案二: 使用
SwiftUI ARView 大小与对齐难题:深度解析与实战方案
IOS
2025-01-11 10:50:20
SwiftUI ARView 大小与对齐问题
在 SwiftUI 中使用 ARKit 的 ARView
时,调整视图的大小和对齐方式有时会变得比较棘手。 当需要将 ARView
以非全屏方式展示时,这些问题可能会尤为明显。下面深入分析这种常见问题的原因,并给出解决方案。
问题分析
首先,观察代码,会发现以下一些潜在问题:
ARView
初始化方式 :ARView
在ARModel
中被初始化,使用了UIScreen.main.bounds
来定义视图大小,然后截取了1/4的高度。这样的初始化操作看似在限定ARView
的尺寸,实际上仅控制了ARView
内容 渲染的区域。它的 frame 并未在整个生命周期中被改变。UIViewRepresentable
的使用 :ARViewContainer
使用UIViewRepresentable
包裹了ARView
,使 UIKit 视图能被集成到 SwiftUI 视图层级。但是,updateUIView
方法并未进行任何的视图尺寸修改。这意味通过 SwiftUI 的布局修改影响不到ARView
的frame
属性。- 错误的对齐和缩放逻辑 : 在
ContentView
中,ARViewContainer
使用了scaledToFill
和固定的高度以及对齐。scaledToFill
会尽可能的铺满设定的容器大小,会忽略ARView
的frame
。这种缩放会影响到最终视图的表现。 ARSession
行为 :ARSession
会尽力捕获并显示摄像头视图,这个渲染输出并不受UIView
的 frame 控制,始终会以初始的frame进行渲染,无论ARView
的 frame 怎么变化。
总结来说,问题根本原因是 ARView
内容渲染和实际的 UIKit frame
概念的混淆,以及 SwiftUI 中直接修改 UIKit
视图布局的限制。
解决方案
以下提供了多个方案,来应对不同的场景需求:
方案一:修改 ARView
的 frame,并调整 SwiftUI 布局
-
原理 : 方案主要是在
ARView
初始化之后,获取其父视图的 Frame, 并利用此参数去重新定义ARView
本身的frame
大小和起始坐标,从而控制视图本身的布局大小。此方案可以有效应对不同尺寸屏幕下视图的自适应需求。 -
步骤 :
- 修改
ARView
初始化逻辑,ARView
的frame
应跟随UIViewController
的尺寸而动态改变。 - 在
ARViewModel
中获取其父视图 Frame,在viewDidLayoutSubviews()
生命周期中调整ARView
的 frame.
- 修改
- SwiftUI 部分布局中只需要使用
.frame()
来限定高度。
- 代码示例:
// 重新定义 MyARView
class MyARView: ARView {
var parentViewFrame:CGRect = .zero
override func layoutSubviews() {
super.layoutSubviews()
if parentViewFrame != self.superview?.frame {
parentViewFrame = self.superview?.frame ?? .zero
let screenRect = UIScreen.main.bounds
let newRect = CGRect(x: screenRect.minX , y:screenRect.minY , width: parentViewFrame.width , height: parentViewFrame.height/4)
print(newRect)
self.frame = newRect
}
}
}
// 修改ARModel
struct ARModel {
private(set) var arView : MyARView
init() {
arView = MyARView()
let config = ARWorldTrackingConfiguration()
config.planeDetection = [.horizontal]
arView.session.run(config)
}
}
struct ARViewContainer : UIViewRepresentable {
var arViewModel: ARViewModel
func makeUIView(context: Context) -> MyARView {
arViewModel.startSessionDelegate()
return arViewModel.arView
}
func updateUIView(_ uiView: MyARView, context: Context) {}
}
struct ContentView: View {
@ObservedObject var arViewModel : ARViewModel = ARViewModel()
@State var mapImage: UIImage?
var body: some View {
ZStack {
if let image = arViewModel.mapImage {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.border(Color.red)
.background(Color.cyan)
} else {
Color.gray
.ignoresSafeArea()
ProgressView()
}
ARViewContainer(arViewModel: arViewModel)
.frame(maxWidth: .infinity)
.frame(height: UIScreen.main.bounds.height / 4)
.border(Color.blue)
.cornerRadius(15)
.padding(25)
Spacer()
}.navigationBarBackButtonHidden(true)
}
}
- 说明 : 修改后的方案通过自定义
ARView
, 并覆写了layoutSubviews
,实现了动态适配,在父视图布局发生变化的时候,会同步修改自身的frame
属性。
方案二: 使用 overlay
实现对齐
- 原理 : 方案使用
overlay
修饰符来实现内容的对齐效果,避免了修改ARView
的frame
。通过对ARView
添加一个遮盖视图,并在遮盖视图中使用alignment: .top
就能达到ARView
视图的top
对齐效果。 - 步骤 :
- 将
ARViewContainer
包装在一个ZStack
中。 - 在
ZStack
中将ARView
和 一个空白视图以overlay
方式布局,空白视图指定对齐方向为.top
。
- 将
- 代码示例
struct ContentView: View {
@ObservedObject var arViewModel : ARViewModel = ARViewModel()
@State var mapImage: UIImage?
var body: some View {
ZStack {
if let image = arViewModel.mapImage {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.border(Color.red)
.background(Color.cyan)
} else {
Color.gray
.ignoresSafeArea()
ProgressView()
}
ZStack {
ARViewContainer(arViewModel: arViewModel)
.frame(maxWidth: .infinity)
.frame(height: UIScreen.main.bounds.height) // 这里必须指定全屏高度
.clipped()
Color.clear
.frame(maxWidth:.infinity)
.frame(height:UIScreen.main.bounds.height / 4) // 定义可见部分高度
.overlay(alignment: .top) {
EmptyView()
}
}
.border(Color.blue)
.cornerRadius(15)
.padding(25)
Spacer()
}.navigationBarBackButtonHidden(true)
}
}
- 说明 : 此方案避免了直接操作
ARView
的frame
, 使用了更加符合 SwiftUI 的组合视图的布局思想来达到布局目的,使用更少的代码完成对齐任务。需要留意ARViewContainer
视图需要充满父视图高度。
安全建议
在操作 ARKit
相关功能时, 尤其是在处理图像帧数据,需要格外注意用户隐私:
- 请求相机权限 : 在使用相机之前,明确请求用户的授权。
- 数据处理 : 在本地处理 ARKit 采集的数据,如避免不必要的数据上传或分享,防止用户隐私泄露。
- 用户提示 : 当用户使用摄像头相关功能时,通过指示器或 UI 反馈明确告知用户当前摄像头的状态,减少用户的误解。
这些方法可以帮助开发者在面对 ARView
大小和对齐的挑战时,有更充分的选择,同时也为应用加入了一定的安全保障。
这些方法覆盖了不同层面的调整策略。应该结合具体的应用场景选择适当的解决方案。合理运用布局的各种方式,可以让 ARView
在 SwiftUI
中展现的更加完美。