fix(iOS): 修复视频显示模式为填充
将远端视频渲染视图和内容视图的视频内容模式从 `.fit` 改为 `.fill`,并调整控制面板布局,将录制与断开按钮单独成行,提升界面利用率与操作体验。
This commit is contained in:
@@ -156,64 +156,72 @@ private struct ControlPanelView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var topBar: some View {
|
private var topBar: some View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
VStack(spacing: 6) {
|
||||||
HStack(spacing: 12) {
|
// 第一行:状态 + 串流模式切换 + 分辨率 + 帧率
|
||||||
Text(viewModel.statusText)
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
.font(.footnote)
|
HStack(spacing: 12) {
|
||||||
.foregroundColor(.green)
|
Text(viewModel.statusText)
|
||||||
.lineLimit(1)
|
.font(.footnote)
|
||||||
|
.foregroundColor(.green)
|
||||||
|
.lineLimit(1)
|
||||||
|
.fixedSize()
|
||||||
|
|
||||||
|
// 串流模式切换(WebRTC / 自编码)
|
||||||
|
Toggle(isOn: Binding(
|
||||||
|
get: { viewModel.streamMode == .selfCodec },
|
||||||
|
set: { viewModel.toggleStreamMode($0) })) {
|
||||||
|
Text("自编码")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
.toggleStyle(.switch)
|
||||||
.fixedSize()
|
.fixedSize()
|
||||||
|
|
||||||
// 串流模式切换(WebRTC / 自编码)
|
// 分辨率选择
|
||||||
Toggle(isOn: Binding(
|
Menu {
|
||||||
get: { viewModel.streamMode == .selfCodec },
|
ForEach(ResolutionOption.all) { option in
|
||||||
set: { viewModel.toggleStreamMode($0) })) {
|
Button {
|
||||||
Text("自编码")
|
viewModel.selectResolution(option)
|
||||||
.font(.footnote)
|
} label: {
|
||||||
.foregroundColor(.white)
|
if option == viewModel.selectedResolution {
|
||||||
}
|
Label(option.title, systemImage: "checkmark")
|
||||||
.toggleStyle(.switch)
|
} else {
|
||||||
.fixedSize()
|
Text(option.title)
|
||||||
|
}
|
||||||
// 分辨率选择
|
|
||||||
Menu {
|
|
||||||
ForEach(ResolutionOption.all) { option in
|
|
||||||
Button {
|
|
||||||
viewModel.selectResolution(option)
|
|
||||||
} label: {
|
|
||||||
if option == viewModel.selectedResolution {
|
|
||||||
Label(option.title, systemImage: "checkmark")
|
|
||||||
} else {
|
|
||||||
Text(option.title)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Label(viewModel.selectedResolution.title, systemImage: "rectangle.compress.vertical")
|
||||||
|
.font(.footnote)
|
||||||
|
.fixedSize()
|
||||||
}
|
}
|
||||||
} label: {
|
|
||||||
Label(viewModel.selectedResolution.title, systemImage: "rectangle.compress.vertical")
|
|
||||||
.font(.footnote)
|
|
||||||
.fixedSize()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 帧率选择(档位来自被控端 REPORT_RESOLUTION 上报)
|
// 帧率选择(档位来自被控端 REPORT_RESOLUTION 上报)
|
||||||
Menu {
|
Menu {
|
||||||
ForEach(viewModel.fpsOptions, id: \.self) { fps in
|
ForEach(viewModel.fpsOptions, id: \.self) { fps in
|
||||||
Button {
|
Button {
|
||||||
viewModel.selectFps(fps)
|
viewModel.selectFps(fps)
|
||||||
} label: {
|
} label: {
|
||||||
if fps == viewModel.currentFps {
|
if fps == viewModel.currentFps {
|
||||||
Label("\(fps)fps", systemImage: "checkmark")
|
Label("\(fps)fps", systemImage: "checkmark")
|
||||||
} else {
|
} else {
|
||||||
Text("\(fps)fps")
|
Text("\(fps)fps")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Label(viewModel.currentFps > 0 ? "\(viewModel.currentFps)fps" : "帧率",
|
||||||
|
systemImage: "speedometer")
|
||||||
|
.font(.footnote)
|
||||||
|
.fixedSize()
|
||||||
}
|
}
|
||||||
} label: {
|
|
||||||
Label(viewModel.currentFps > 0 ? "\(viewModel.currentFps)fps" : "帧率",
|
|
||||||
systemImage: "speedometer")
|
|
||||||
.font(.footnote)
|
|
||||||
.fixedSize()
|
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
.padding(.vertical, 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二行:录制 + 断开(当前行下方)
|
||||||
|
HStack(spacing: 12) {
|
||||||
// 录制远端视频(MP4)
|
// 录制远端视频(MP4)
|
||||||
Button {
|
Button {
|
||||||
if viewModel.isRecording {
|
if viewModel.isRecording {
|
||||||
@@ -241,9 +249,11 @@ private struct ControlPanelView: View {
|
|||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
.tint(.red)
|
.tint(.red)
|
||||||
.controlSize(.small)
|
.controlSize(.small)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.padding(.vertical, 6)
|
.padding(.bottom, 6)
|
||||||
}
|
}
|
||||||
.background(Color(white: 0.1))
|
.background(Color(white: 0.1))
|
||||||
}
|
}
|
||||||
@@ -269,7 +279,7 @@ private struct ControlPanelView: View {
|
|||||||
viewModel.sendSwipe(x1: x1, y1: y1, x2: x2, y2: y2, duration: duration)
|
viewModel.sendSwipe(x1: x1, y1: y1, x2: x2, y2: y2, duration: duration)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
.aspectRatio(viewModel.videoAspect, contentMode: .fit)
|
.aspectRatio(viewModel.videoAspect, contentMode: .fill)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
|
||||||
// 录制中指示(左上角红点)
|
// 录制中指示(左上角红点)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ struct RemoteVideoView: UIViewRepresentable {
|
|||||||
let videoView: RTCMTLVideoView
|
let videoView: RTCMTLVideoView
|
||||||
|
|
||||||
func makeUIView(context: Context) -> RTCMTLVideoView {
|
func makeUIView(context: Context) -> RTCMTLVideoView {
|
||||||
videoView.videoContentMode = .scaleAspectFit
|
videoView.videoContentMode = .scaleAspectFill
|
||||||
return videoView
|
return videoView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user