From bf9e4680eb466bcb7c9cb1bef567252bb1f2bb7d Mon Sep 17 00:00:00 2001
From: Runt <qingingrunt2010@qq.com>
Date: Fri, 27 Jun 2025 05:32:55 +0000
Subject: [PATCH] 1
---
LiveProject/views/VideoRendererView.swift | 58 +----
LiveProject/styles/TextBtnStyle.swift | 2
LiveProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 15 +
LiveProject.xcodeproj/project.pbxproj | 28 ++
LiveProject/activity/stream/LiveActivity.swift | 128 +++++++++++-
LiveProject/enum/StreamType.swift | 23 ++
LiveProject/tool/MetalRenderer.swift | 78 +-----
LiveProject/data/DeviceInfo.swift | 21 ++
LiveProject/views/FlowLayout.swift | 193 +++++++++++++++++++
LiveProject/views/LButton.swift | 1
LiveProject/views/MButton.swift | 1
LiveProject.xcodeproj/xcuserdata/nilupeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist | 6
LiveProject/shape/IconBack.swift | 30 ++
LiveProject/views/TitleBarView.swift | 9
14 files changed, 466 insertions(+), 127 deletions(-)
diff --git a/LiveProject.xcodeproj/project.pbxproj b/LiveProject.xcodeproj/project.pbxproj
index a56e128..f659319 100644
--- a/LiveProject.xcodeproj/project.pbxproj
+++ b/LiveProject.xcodeproj/project.pbxproj
@@ -6,6 +6,10 @@
objectVersion = 77;
objects = {
+/* Begin PBXBuildFile section */
+ A1615DB82E0D9A6E00D73CE3 /* WrappingHStack in Frameworks */ = {isa = PBXBuildFile; productRef = A1615DB72E0D9A6E00D73CE3 /* WrappingHStack */; };
+/* End PBXBuildFile section */
+
/* Begin PBXContainerItemProxy section */
A1615D372E0C27F700D73CE3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
@@ -42,6 +46,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ A1615DB82E0D9A6E00D73CE3 /* WrappingHStack in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -100,6 +105,7 @@
);
name = LiveProject;
packageProductDependencies = (
+ A1615DB72E0D9A6E00D73CE3 /* WrappingHStack */,
);
productName = LiveProject;
productReference = A1615D292E0C27F500D73CE3 /* LiveProject.app */;
@@ -177,6 +183,9 @@
);
mainGroup = A1615D202E0C27F500D73CE3;
minimizedProjectReferenceProxies = 1;
+ packageReferences = (
+ A1615DB62E0D996500D73CE3 /* XCRemoteSwiftPackageReference "WrappingHStack" */,
+ );
preferredProjectObjectVersion = 77;
productRefGroup = A1615D2A2E0C27F500D73CE3 /* Products */;
projectDirPath = "";
@@ -532,6 +541,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ A1615DB62E0D996500D73CE3 /* XCRemoteSwiftPackageReference "WrappingHStack" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/dkk/WrappingHStack";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 2.2.11;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ A1615DB72E0D9A6E00D73CE3 /* WrappingHStack */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = A1615DB62E0D996500D73CE3 /* XCRemoteSwiftPackageReference "WrappingHStack" */;
+ productName = WrappingHStack;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = A1615D212E0C27F500D73CE3 /* Project object */;
}
diff --git a/LiveProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LiveProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000..5ac2f71
--- /dev/null
+++ b/LiveProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,15 @@
+{
+ "originHash" : "50578bb08258a739890c5d3d0a495ba4213b27b4dbbbc844c23e93aa2d80550b",
+ "pins" : [
+ {
+ "identity" : "wrappinghstack",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/dkk/WrappingHStack",
+ "state" : {
+ "revision" : "425d9488ba55f58f0b34498c64c054c77fc2a44b",
+ "version" : "2.2.11"
+ }
+ }
+ ],
+ "version" : 3
+}
diff --git a/LiveProject.xcodeproj/xcuserdata/nilupeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/LiveProject.xcodeproj/xcuserdata/nilupeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..6bd2c4d
--- /dev/null
+++ b/LiveProject.xcodeproj/xcuserdata/nilupeng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+ uuid = "B6E33E15-95B1-4036-89E0-5C9B0FA33668"
+ type = "1"
+ version = "2.0">
+</Bucket>
diff --git a/LiveProject/activity/stream/LiveActivity.swift b/LiveProject/activity/stream/LiveActivity.swift
index d6faae2..8f16308 100644
--- a/LiveProject/activity/stream/LiveActivity.swift
+++ b/LiveProject/activity/stream/LiveActivity.swift
@@ -7,21 +7,121 @@
import Foundation
import SwiftUI
+import WrappingHStack
struct LiveActivity: View {
+ @State private var pixelBuffer: CVPixelBuffer?
+
+
+ @State private var showDeviceDialog = false
+
+ @State private var streamRate = Float(9/16.0);
+ @State private var mainSize: CGSize = .init(width: 100, height: 100)
+
+ @State private var devices = [DeviceInfo(name: "相机", type: .CAMERA(), deviceId: "相机"),
+ DeviceInfo(name: "话筒", type: .MICROPHONE(),deviceId: "话筒"),
+ DeviceInfo(name: "系统", type: .SYSTEM(),deviceId : "系统")]
+
var body: some View {
- NavigationView{
- ZStack{
+ ZStack{
+ Color.clear
+ .ignoresSafeArea() // 填满全屏
+ VStack{
+ VideoRendererView(renderer: MetalRenderer()).background(Color.black).frame(width: mainSize.width,height:mainSize.height)
+ Spacer()
+ }.border(Color.blue)
+ VStack{
+ Spacer()
+ BottomBtns().frame(alignment: .bottom).border(Color.green)
+ }
+ if showDeviceDialog {
+ DialogDevices()
+ }
+ }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
+ .background(
+ GeometryReader { geometry in
+ Color.clear
+ .onAppear {
+ updateWindowSize(width: Int(geometry.size.width), height: Int(geometry.size.height))
+ }
+ .onChange(of: geometry.size) { newSize in
+ updateWindowSize(width: Int(newSize.width), height: Int(newSize.height))
+ }
+ })
+ .border(Color.red)
+ .onDisappear {
+ print("onDisappear 视图消失了!")
+ }.onAppear {
+ print("onAppear 视图出现了!")
+ }
+ }
+
+ func updateWindowSize(width:Int,height:Int){
+ var rate : Float = Float(width)/Float(height);
+ if(rate != streamRate) {
+ var mainWidth = 0;
+ var mainHeight = 0;
+ if(rate < streamRate){
+ mainWidth = width;
+ if(9.0/16 == streamRate){
+ mainHeight = (mainWidth / 9 * 16);
+ }else{
+ mainHeight = (mainWidth / 16 * 9);
+ }
+ }else{
+ mainHeight = height;
+ if(9.0 / 16 == streamRate){
+ mainWidth = (mainHeight / 16 * 9);
+ }else{
+ mainWidth = (mainHeight / 9 * 16);
+ }
+ }
+ if(mainSize.width != CGFloat(mainWidth) || mainSize.height != CGFloat(mainHeight)){
+ mainSize = .init(width: mainWidth, height: mainHeight);
+ }
+ }else{
+ if(mainSize.width != CGFloat(width) || mainSize.height != CGFloat(height)){
+ mainSize = .init(width: width, height: height);
}
}
+ //Log.w(TAG , "onSizeChanged: ${mainWindowSize.value}" , )
}
-}
-
-struct Bottombtns:View {
- var body: some View {
+
+ func DialogDevices() -> some View{
+ ZStack{
+ Color.black.opacity(0.4)
+ .edgesIgnoringSafeArea(.all)
+ .onTapGesture {
+ withAnimation {
+ showDeviceDialog = false
+ }
+ }
+ VStack {
+ Spacer()
+ VStack(spacing: 20) {
+ Spacer().frame(height:40)
+ FlowLayout(devices){ device in
+ MButton(text:device.name){
+
+ }
+ }
+ .padding()
+ .animation(.default, value: devices)
+ }
+ .frame(maxWidth: .infinity)
+ .padding()
+ .background(Color.white)
+ .cornerRadius(20)
+ .transition(.move(edge: .bottom))
+ }
+ .zIndex(1)
+ }
+ }
+
+ func BottomBtns() -> some View{
VStack{
-
+
HStack(){
MButton(icon: IconPortrait()){
@@ -35,14 +135,17 @@
}
HStack{
LButton(text: "设备"){
-
+ print("Click 设备 button")
+ withAnimation{
+ showDeviceDialog.toggle()
+ }
}
LButton(text: "RTMP"){
}
- LButton(text: "文件"){
+ /*flLButton(text: "文件"){
- }
+ }*/
LButton(text: "文本"){
}
@@ -56,9 +159,10 @@
}
}
-struct LiveActivity_Previews: PreviewProvider{
+
+struct LiveActivity_BottomBtns_Previews: PreviewProvider{
static var previews: some View {
- Bottombtns();
+ LiveActivity();
}
}
diff --git a/LiveProject/data/DeviceInfo.swift b/LiveProject/data/DeviceInfo.swift
index e69de29..e3313f4 100644
--- a/LiveProject/data/DeviceInfo.swift
+++ b/LiveProject/data/DeviceInfo.swift
@@ -0,0 +1,21 @@
+//
+// DeviceInfo.swift
+// LiveProject
+//
+// Created by 倪路朋 on 6/26/25.
+//
+
+
+struct DeviceInfo : Hashable {
+ let name:String
+ let type:StreamType
+ let deviceId:String;
+
+ func hash(into hasher: inout Hasher){
+ hasher.combine(deviceId)
+ }
+
+ static func == (lhs: DeviceInfo, rhs: DeviceInfo) -> Bool {
+ lhs.deviceId == rhs.deviceId
+ }
+}
diff --git a/LiveProject/enum/StreamType.swift b/LiveProject/enum/StreamType.swift
index e69de29..c2c1254 100644
--- a/LiveProject/enum/StreamType.swift
+++ b/LiveProject/enum/StreamType.swift
@@ -0,0 +1,23 @@
+//
+// StreamType.swift
+// LiveProject
+//
+// Created by 倪路朋 on 6/26/25.
+//
+
+
+enum StreamType{
+ case
+ //系统
+ SYSTEM(val:Int = 0),
+ //本地类型
+ FILE(val:Int = 1),
+ //图像
+ CAMERA(val:Int = 100),VIDEO(val:Int = 102),PICTURE(val:Int = 104),TEXT(val:Int = 106),
+ //音频
+ MICROPHONE(val:Int = 101),AUDIO(val:Int = 103),
+ //外置设备
+ UVC(val:Int = 301),UAC(val:Int = 302),
+ //远程
+ RTMP(val:Int = 200)
+}
diff --git a/LiveProject/shape/IconBack.swift b/LiveProject/shape/IconBack.swift
index 0856c3b..5d760f8 100644
--- a/LiveProject/shape/IconBack.swift
+++ b/LiveProject/shape/IconBack.swift
@@ -5,4 +5,32 @@
// Created by 倪路朋 on 6/26/25.
//
-import Foundation
+import SwiftUI
+
+struct IconBack: Shape {
+
+ func path(in rect: CGRect) -> Path {
+ var path = Path()
+
+ let arrowHeadLength = rect.width * 0.4
+ let shaftY = rect.midY
+ let headHeight = rect.height * 0.5
+
+ // 箭头头部(三角形)
+ path.move(to: CGPoint(x: arrowHeadLength, y: rect.minY))
+ path.addLine(to: CGPoint(x: 0, y: shaftY))
+ path.addLine(to: CGPoint(x: arrowHeadLength, y: rect.maxY))
+
+ // 箭身(横线)
+ path.move(to: CGPoint(x: 0, y: shaftY))
+ path.addLine(to: CGPoint(x: rect.maxX, y: shaftY))
+ return path
+ }
+}
+
+struct IconBack_Previews : PreviewProvider{
+ static var previews: some View {
+ IconBack()
+ .stroke(Color.primary, lineWidth: 3).frame(width: 30,height: 25)
+ }
+}
diff --git a/LiveProject/styles/TextBtnStyle.swift b/LiveProject/styles/TextBtnStyle.swift
index 7c22fda..85e0369 100644
--- a/LiveProject/styles/TextBtnStyle.swift
+++ b/LiveProject/styles/TextBtnStyle.swift
@@ -11,5 +11,7 @@
struct TextBtnStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
+ .scaleEffect(configuration.isPressed ? 0.95 : 1.0)
+ .opacity(configuration.isPressed ? 0.8 : 1.0)
}
}
diff --git a/LiveProject/tool/MetalRenderer.swift b/LiveProject/tool/MetalRenderer.swift
index 3e88871..791dfc8 100644
--- a/LiveProject/tool/MetalRenderer.swift
+++ b/LiveProject/tool/MetalRenderer.swift
@@ -4,75 +4,35 @@
// 渲染工具
// Created by 倪路朋 on 6/26/25.
//
+import CoreVideo
import Metal
import MetalKit
-class Renderer: NSObject, MTKViewDelegate {
- static var shared: Renderer?
+class MetalRenderer: NSObject, MTKViewDelegate {
+ var device: MTLDevice!
+ var commandQueue: MTLCommandQueue!
- private var device: MTLDevice!
- private var commandQueue: MTLCommandQueue!
- private var pipelineState: MTLRenderPipelineState!
- private var texture: MTLTexture?
- private var textureDescriptor: MTLTextureDescriptor!
- private var currentData: [UInt8]?
- private var textureWidth = 0
- private var textureHeight = 0
-
- weak var mtkView: MTKView? {
- didSet {
- guard let view = mtkView else { return }
- device = view.device
- commandQueue = device.makeCommandQueue()
-
- let library = device.makeDefaultLibrary()
- let pipelineDesc = MTLRenderPipelineDescriptor()
- pipelineDesc.vertexFunction = library?.makeFunction(name: "vertexShader")
- pipelineDesc.fragmentFunction = library?.makeFunction(name: "fragmentShader")
- pipelineDesc.colorAttachments[0].pixelFormat = view.colorPixelFormat
-
- pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDesc)
-
- Renderer.shared = self
- }
+ func setup(view: MTKView) {
+ self.device = view.device
+ self.commandQueue = device.makeCommandQueue()
+ // 初始化 texture、pipeline 等
}
- func updateFrame(data: [UInt8], width: Int, height: Int) {
- currentData = data
- textureWidth = width
- textureHeight = height
+ // ✅ 必须实现的方法 1:窗口大小改变时调用
+ func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
+ // 可以留空或更新视图缩放、渲染区域等
}
+ // ✅ 必须实现的方法 2:每一帧绘制时调用
func draw(in view: MTKView) {
guard let drawable = view.currentDrawable,
- let descriptor = view.currentRenderPassDescriptor,
- let data = currentData else { return }
+ let descriptor = view.currentRenderPassDescriptor else { return }
- if texture == nil || texture?.width != textureWidth || texture?.height != textureHeight {
- textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
- pixelFormat: .rgba8Unorm,
- width: textureWidth,
- height: textureHeight,
- mipmapped: false
- )
- textureDescriptor.usage = [.shaderRead, .shaderWrite]
- texture = device.makeTexture(descriptor: textureDescriptor)
- }
-
- let region = MTLRegionMake2D(0, 0, textureWidth, textureHeight)
- data.withUnsafeBytes { ptr in
- texture?.replace(region: region, mipmapLevel: 0, withBytes: ptr.baseAddress!, bytesPerRow: textureWidth * 4)
- }
-
- let commandBuffer = commandQueue.makeCommandBuffer()
- let encoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor)
- encoder?.setRenderPipelineState(pipelineState)
- encoder?.setFragmentTexture(texture, index: 0)
- encoder?.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
- encoder?.endEncoding()
- commandBuffer?.present(drawable)
- commandBuffer?.commit()
+ let commandBuffer = commandQueue.makeCommandBuffer()!
+ let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor)!
+ // 渲染逻辑,如绘制纹理
+ encoder.endEncoding()
+ commandBuffer.present(drawable)
+ commandBuffer.commit()
}
-
- func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
}
diff --git a/LiveProject/views/FlowLayout.swift b/LiveProject/views/FlowLayout.swift
index f3ee86a..151cd68 100644
--- a/LiveProject/views/FlowLayout.swift
+++ b/LiveProject/views/FlowLayout.swift
@@ -4,4 +4,197 @@
//
// Created by 倪路朋 on 6/26/25.
//
+import SwiftUI
+import SwiftUI
+/// 完全自定义的自动换行布局容器
+struct FlowLayout<Data: RandomAccessCollection, Content: View>: View where Data.Element: Hashable {
+ // MARK: - 属性
+
+ /// 要显示的数据集合
+ let data: Data
+ /// 水平间距
+ let horizontalSpacing: CGFloat
+ /// 垂直间距
+ let verticalSpacing: CGFloat
+ /// 对齐方式
+ let alignment: HorizontalAlignment
+ /// 内容构建闭包
+ let content: (Data.Element) -> Content
+ /// 总高度状态
+ @State private var totalHeight: CGFloat = 0
+
+ // MARK: - 初始化
+
+ /// 初始化FlowLayout
+ /// - Parameters:
+ /// - data: 要显示的数据集合
+ /// - horizontalSpacing: 水平间距,默认为8
+ /// - verticalSpacing: 垂直间距,默认为8
+ /// - alignment: 对齐方式,默认为.leading
+ /// - content: 内容构建闭包
+ init(
+ _ data: Data,
+ horizontalSpacing: CGFloat = 8,
+ verticalSpacing: CGFloat = 8,
+ alignment: HorizontalAlignment = .leading,
+ @ViewBuilder content: @escaping (Data.Element) -> Content
+ ) {
+ self.data = data
+ self.horizontalSpacing = horizontalSpacing
+ self.verticalSpacing = verticalSpacing
+ self.alignment = alignment
+ self.content = content
+ }
+
+ // MARK: - 主体视图
+
+ var body: some View {
+ GeometryReader { geometry in
+ self.contentView(in: geometry)
+ .background(
+ HeightReader(height: $totalHeight)
+ )
+ }
+ .frame(height: totalHeight)
+ }
+
+ // MARK: - 私有方法
+
+ /// 构建内容视图
+ private func contentView(in geometry: GeometryProxy) -> some View {
+ var width: CGFloat = 0
+ var height: CGFloat = 0
+ var lastHeight: CGFloat = 0
+
+ return ZStack(alignment: Alignment(horizontal: alignment, vertical: .top)) {
+ ForEach(data.map { $0 }, id: \.self) { item in
+ content(item)
+ .padding(.trailing, horizontalSpacing)
+ .padding(.bottom, verticalSpacing)
+ .alignmentGuide(.leading) { dimensions in
+ // 检查是否需要换行
+ if abs(width - dimensions.width) > geometry.size.width {
+ width = 0
+ height += lastHeight + verticalSpacing
+ }
+
+ let result = width
+
+ // 更新宽度计算
+ if item == data.last {
+ width = 0 // 重置为0,最后一项
+ } else {
+ width -= dimensions.width + horizontalSpacing
+ }
+
+ // 记录当前行高度
+ lastHeight = dimensions.height
+ return result
+ }
+ .alignmentGuide(.top) { dimensions in
+ let result = height
+
+ // 如果是最后一项,更新总高度
+ if item == data.last {
+ height += lastHeight + verticalSpacing
+ }
+
+ return result
+ }
+ }
+ }
+ }
+}
+
+// MARK: - 高度读取器
+
+/// 用于读取视图高度的辅助视图
+private struct HeightReader: View {
+ @Binding var height: CGFloat
+
+ var body: some View {
+ GeometryReader { geometry in
+ Color.clear
+ .preference(
+ key: HeightPreferenceKey.self,
+ value: geometry.size.height
+ )
+ }
+ .onPreferenceChange(HeightPreferenceKey.self) { newHeight in
+ DispatchQueue.main.async {
+ self.height = newHeight
+ }
+ }
+ }
+}
+
+// MARK: - 高度偏好键
+
+/// 用于传递高度值的PreferenceKey
+private struct HeightPreferenceKey: PreferenceKey {
+ static var defaultValue: CGFloat = 0
+
+ static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
+ value = nextValue()
+ }
+}
+
+// MARK: - 使用示例
+
+struct FlowLayoutExample: View {
+ let tags = [
+ "Swift", "SwiftUI", "UIKit", "Combine", "Core Data",
+ "Xcode", "Interface Builder", "Core Animation", "ARKit",
+ "Metal", "Core ML", "Vision", "MapKit", "CloudKit"
+ ]
+
+ @State private var newTag = ""
+ @State private var customTags = ["自定义标签1", "自定义标签2"]
+
+ var body: some View {
+ VStack {
+ FlowLayout(customTags + tags, horizontalSpacing: 10, verticalSpacing: 10) { tag in
+ MButton(text:tag){
+
+ }
+ }
+ .padding()
+ .animation(.default, value: customTags)
+ }
+ }
+
+ private func addTag() {
+ withAnimation {
+ customTags.append(newTag)
+ newTag = ""
+ }
+ }
+}
+
+/// 标签视图
+struct TagView: View {
+ let text: String
+
+ var body: some View {
+ Text(text)
+ .padding(.horizontal, 12)
+ .padding(.vertical, 8)
+ .background(Color.blue.opacity(0.2))
+ .foregroundColor(.blue)
+ .cornerRadius(10)
+ .overlay(
+ RoundedRectangle(cornerRadius: 10)
+ .stroke(Color.blue, lineWidth: 1)
+ )
+ .fixedSize(horizontal: true, vertical: false) // 确保文本不被截断
+ }
+}
+
+// MARK: - 预览
+
+struct FlowLayout_Previews: PreviewProvider {
+ static var previews: some View {
+ FlowLayoutExample()
+ }
+}
diff --git a/LiveProject/views/LButton.swift b/LiveProject/views/LButton.swift
index 932ff21..6f430e2 100644
--- a/LiveProject/views/LButton.swift
+++ b/LiveProject/views/LButton.swift
@@ -21,7 +21,6 @@
}
var body: some View {
- //登录按钮
Button(action: {
if(valid == .INVALID){
return
diff --git a/LiveProject/views/MButton.swift b/LiveProject/views/MButton.swift
index 06da285..ce8b915 100644
--- a/LiveProject/views/MButton.swift
+++ b/LiveProject/views/MButton.swift
@@ -21,7 +21,6 @@
}
var body: some View {
- //登录按钮
Button(action: {
if(valid == .INVALID){
return
diff --git a/LiveProject/views/TitleBarView.swift b/LiveProject/views/TitleBarView.swift
index a58fd40..7cfce53 100644
--- a/LiveProject/views/TitleBarView.swift
+++ b/LiveProject/views/TitleBarView.swift
@@ -12,9 +12,9 @@
@Environment(\.presentationMode) var presentationMode
var title = ""
- var imgLeft = "IconBack";
+ var iconBack = IconBack();
var imgRight = "";
- var titleColor = "ColorText"
+ var titleColor = Color.colorText
var body: some View {
@@ -24,11 +24,10 @@
print("Click back button")
self.presentationMode.wrappedValue.dismiss()
}) {
- Image(imgLeft)
- .frame(width: 28,height: 28)
+ iconBack.stroke(Color.primary, lineWidth: 2.5).frame(width: 18,height: 14)
}
Spacer()
- Text(title).foregroundColor(Color.init(titleColor))
+ Text(title).foregroundColor(titleColor)
.bold().font(Font.system(size: 16))
Spacer()
Image(imgRight).frame(width: 28,height: 28)
diff --git a/LiveProject/views/VideoRendererView.swift b/LiveProject/views/VideoRendererView.swift
index 960aa78..f3b0341 100644
--- a/LiveProject/views/VideoRendererView.swift
+++ b/LiveProject/views/VideoRendererView.swift
@@ -7,57 +7,19 @@
import SwiftUI
import MetalKit
-struct MetalVideoRenderView: UIViewRepresentable {
- // 统一接口,接收各种视频源的帧数据
- var frameData: VideoFrameData?
-
- func makeCoordinator() -> Coordinator {
- Coordinator(self)
- }
-
+struct VideoRendererView: UIViewRepresentable {
+ let renderer: MetalRenderer // 自定义 Metal 渲染器,支持传入 RGBA/YUV 数据帧
+
func makeUIView(context: Context) -> MTKView {
let mtkView = MTKView()
- mtkView.device = context.coordinator.device
- mtkView.delegate = context.coordinator
+ mtkView.device = MTLCreateSystemDefaultDevice()
mtkView.framebufferOnly = false
- mtkView.colorPixelFormat = .bgra8Unorm
- mtkView.autoResizeDrawable = true
+ mtkView.enableSetNeedsDisplay = false
+ mtkView.isPaused = true
+ mtkView.delegate = renderer
+ renderer.setup(view: mtkView)
return mtkView
}
-
- func updateUIView(_ uiView: MTKView, context: Context) {
- // 更新帧数据
- context.coordinator.updateFrame(frameData)
- }
-
- class Coordinator: NSObject, MTKViewDelegate {
- var parent: MetalVideoRenderView
- var device: MTLDevice
- var commandQueue: MTLCommandQueue
- var pipelineState: MTLRenderPipelineState
- var textureCache: CVMetalTextureCache?
-
- // YUV转RGB的着色器
- var yuvConversionPipeline: MTLComputePipelineState?
-
- init(_ parent: MetalVideoRenderView) {
- self.parent = parent
- self.device = MTLCreateSystemDefaultDevice()!
- self.commandQueue = device.makeCommandQueue()!
-
- // 初始化Metal资源
- // (这里需要设置渲染管线和纹理缓存)
- }
-
- func updateFrame(_ frameData: VideoFrameData?) {
- // 根据不同的帧数据格式(YUV/RGBA)进行处理
- // 将数据转换为Metal纹理
- }
-
- func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
-
- func draw(in view: MTKView) {
- // 执行Metal绘制命令
- }
- }
+
+ func updateUIView(_ uiView: MTKView, context: Context) {}
}
--
Gitblit v1.9.1