From 7b3ecfffc59d2d980d9f7628365b64c20fe015be Mon Sep 17 00:00:00 2001
From: Runt <qingingrunt2010@qq.com>
Date: Sun, 27 Jul 2025 09:42:03 +0000
Subject: [PATCH] 多个小窗缩放问题修复

---
 LiveProject/activity/stream/views/MiniWindow.swift |  138 ++++++++++++++++++++++++++++++++++++----------
 1 files changed, 108 insertions(+), 30 deletions(-)

diff --git a/LiveProject/activity/stream/views/MiniWindow.swift b/LiveProject/activity/stream/views/MiniWindow.swift
index 357e6a0..596b66d 100644
--- a/LiveProject/activity/stream/views/MiniWindow.swift
+++ b/LiveProject/activity/stream/views/MiniWindow.swift
@@ -26,22 +26,36 @@
     }
     
     @Published var pixelBuffer: CVPixelBuffer? = nil
-    @Published var size:CGSize = CGSize(width:300,height:200)
-    @Published var position:CGSize = CGSize(width: 0, height: 0)
+    //流尺寸(图片和视频的分辨率)
+    @Published var size:CGSize = CGSize(width:16,height:9)
+    //视图比例(相对于 main 尺寸)
+    @Published var viewRate = 0.5
+    //位置
+    @Published var position:CGPoint = CGPoint(x: 0, y: 0)
+    //主选框位置
     @Published var mainPosition:CGSize = CGSize(width: 0, height: 0)
+    //main 尺寸
     @Published var mainSize:CGSize = CGSize(width:200,height:100)
+    //是否加入直播
     @Published var videoState:LiveState = .IN_LIVE
     @Published var audioState:LiveState = .IN_LIVE
+    //是否静音
     @Published var speakerState:MuteState = .UN_MUTE
+    //包含视频和图片
     @Published var hasVideo = true;
+    //包含音频
     @Published var hasAudio = true;
+    //旋转角度
     @Published var rotate = 0;
+    //fps
     @Published var frameFPS = 0;
+    //
     @Published var name = " 名称";
+    //相机焦距
     @Published var minZoom = 0;
     @Published var maxZoom = 0;
     @Published var zoom = 1.0
-    
+    //文本颜色
     @Published var textColor = Color.white;
 }
 struct MiniWindowActions {
@@ -72,11 +86,25 @@
     
     @ObservedObject var miniData:MiniWindowData
     @State var lastLocation = CGSize(width: 0, height: 0)
+    @State var lastZoom = 0.0
     @Environment(\.miniWindowActions) var actions
     
     var body: some View {
-        ZStack(){
-            VideoRendererView(pixelBuffer: $miniData.pixelBuffer).background(Color.black)
+        var width = (miniData.mainSize.width * miniData.viewRate);
+        var height = (miniData.mainSize.width / (miniData.size.width / miniData.size.height) * miniData.viewRate);
+        if(miniData.rotate == 90 || miniData.rotate == 270){
+            var temp = width;
+            width = height;
+            height = temp;
+        }
+        return ZStack(){
+            let optionalIntBinding = Binding<Int?>(
+                get: { miniData.rotate },
+                set: { newValue in
+                    miniData.rotate = newValue ?? 0
+                }
+            )
+            VideoRendererView(pixelBuffer: $miniData.pixelBuffer,rotate:optionalIntBinding).background(Color.black)
             
             //名称
             Text(self.miniData.name)
@@ -104,44 +132,72 @@
                 .clipShape(RoundedCorner(radius: 20,corners: [.bottomRight]))
                 .frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topLeading)
             //关闭
-            IconButton(info: Icons.CLOSE,action:actions.onCloseClick).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topTrailing)
-                .padding(EdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 10))
+            IconButton(info: Icons.CLOSE,action:actions.onCloseClick,width: 40,height: 40).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topTrailing)
+                .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
             if(self.miniData.streamType != .TEXT && self.miniData.hasVideo){
                 //旋转
                 VStack{
                     
                     Spacer()
-                    IconButton(info: Icons.ROTATE_LEFT,action:actions.onRotateLeftClick).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 0))
+                    IconButton(info: Icons.ROTATE_LEFT,action:{
+                        if (self.miniData.rotate == 0) {
+                            self.miniData.rotate = 270
+                        } else {
+                            self.miniData.rotate -= 90
+                        }
+                        actions.onRotateLeftClick()
+                    },width: 40,height: 40).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 0))
                     Spacer()
-                    IconButton(info: Icons.ROTATE_RIGHT,action:actions.onRotateRightClick).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 11))
+                    IconButton(info: Icons.ROTATE_RIGHT,action:{
+                        self.miniData.rotate = (self.miniData.rotate + 90) % 360
+                        actions.onRotateRightClick()
+                    },width: 40,height: 40).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 11))
                     Spacer()
                 }.frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topTrailing)
-                    .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10))
+                    .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
             }
             
-            BottomBtns().background(Color.gray.opacity(0.4)).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .bottom)
+            BottomBtns().background(Color.black.opacity(0.4)).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .bottom)
             
-        }.background(Color.blue).frame(width: miniData.size.width,height:miniData.size.height)
-            .offset(self.miniData.position)
-            .gesture(DragGesture(minimumDistance: 0)
+        }.background(Color.blue).frame(width:width,height:height)
+            .position(self.miniData.position)
+            .gesture(SimultaneousGesture(DragGesture(minimumDistance: 0)
                 .onChanged{ val in
-                    let x = self.miniData.position.width
-                    let y = self.miniData.position.height
+                    let x = self.miniData.position.x
+                    let y = self.miniData.position.y
                     let newX = x - lastLocation.width + val.translation.width;
                     let newY = y - lastLocation.height + val.translation.height;
-                    self.miniData.position = CGSize(width: newX, height: newY)
-                    print(" onChanged \(index) \(self.miniData.position) ")
+                    self.miniData.position = CGPoint(x: newX, y: newY)
+                    //print(" onChanged \(index) \(self.miniData.position) ")
                     lastLocation = val.translation;
                 }.onEnded{ val in
-                    let x = self.miniData.position.width
-                    let y = self.miniData.position.height
+                    let x = self.miniData.position.x
+                    let y = self.miniData.position.y
                     let newX = x - lastLocation.width + val.translation.width;
                     let newY = y - lastLocation.height + val.translation.height;
-                    self.miniData.position = CGSize(width: newX, height: newY)
-                    print(" onChanged \(index) \(self.miniData.position) ")
+                    self.miniData.position = CGPoint(x: newX, y: newY)
+                    //print(" onChanged \(index) \(self.miniData.position) ")
                     lastLocation = CGSize(width: 0, height: 0);
-                    print(" onEnded \(val)")
-                })
+                    //print(" onEnded \(val)")
+                },MagnifyGesture().onChanged{ val in
+                    if(lastZoom > 0){
+                        var l = val.magnification - lastZoom;
+                        miniData.viewRate += l;
+                        if(miniData.viewRate < 0.4){
+                            miniData.viewRate = 0.4
+                        }else{
+                        }
+                    }
+                    lastZoom = val.magnification;
+                }.onEnded{ val in
+                    var l = val.magnification - lastZoom;
+                    miniData.viewRate += l;
+                    if(miniData.viewRate < 0.4){
+                        miniData.viewRate = 0.4
+                    }else{
+                    }
+                    lastZoom = 0;
+                }))
     }
     
     /**
@@ -179,7 +235,7 @@
                     actions.onMicClick()
                 }
                 Spacer()
-                if(self.miniData.streamType != .MICROPHONE && self.miniData.streamType != .UAC && self.miniData.streamType != .SYSTEM ){
+                if(self.miniData.streamType != .MICROPHONE && self.miniData.streamType != .UAC && self.miniData.streamType != .SYSTEM && self.miniData.streamType != .CAMERA){
                     IconButton(info: self.miniData.speakerState == .UN_MUTE ? Icons.SPEAKER : Icons.SPEAKER_MUTE){
                         if(self.miniData.speakerState == .MUTE){
                             self.miniData.speakerState = .UN_MUTE
@@ -201,8 +257,8 @@
         }.frame(maxWidth: .infinity)
             .padding(EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15))
     }
-    
-    func IconButton(info:IconInfo,action:@escaping ()->Void = {}, disabled:Bool = true,allow:Bool = true) -> some View{
+
+    func IconButton(info:IconInfo,action:@escaping ()->Void = {},width:CGFloat = 20,height:CGFloat = 20, disabled:Bool = true,allow:Bool = true) -> some View{
         Button(action:{
             print("IconButton ")
             action()
@@ -214,7 +270,8 @@
                     .frame(width: info.size.width, height: info.size.height)
                     .aspectRatio(contentMode: .fit)
                     .foregroundColor(Color.white)
-            }.frame(width: 30,height: 30)
+            }.frame(width: width,height: height)
+                .contentShape(Rectangle()) // 明确指定可点击区域
         }.buttonStyle(TextBtnStyle())
             //.allowsHitTesting(allow) // ✅ 完全禁用点击(不可交互,不触发,不高亮)
             //.disabled(disabled) // ✅ 禁用点击事件,但按钮可能变灰(系统默认行为)
@@ -286,14 +343,35 @@
 struct MiniWindow_BottomBtns_Previews: PreviewProvider{
     @EnvironmentObject var miniData:MiniWindowData
     static var previews: some View {
-        var miniData = MiniWindowData(streamType: StreamType.CAMERA)
+        var miniData = MiniWindowData(streamType: StreamType.CAMERA);
         var miniDataBinding = Binding<MiniWindowData> {
+            miniData.hasAudio = false;
             return miniData
         } set: { MiniWindowData, Transaction in
             
         }
         
-        MiniWindow(miniData:miniData).environmentObject(miniData);
+        MiniWindow(miniData:miniData).frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
+            .background(
+                GeometryReader { geometry in
+                    Color.clear
+                        .onAppear {
+                            miniData.mainSize = geometry.size;
+                            
+                            print("displaySize:\(miniData.mainSize)")
+                        }
+                        .onChange(of: geometry.size) { newSize in
+                            miniData.mainSize = newSize;
+                            print("displaySize:\(miniData.mainSize)")
+                        }
+                })
+            .border(Color.red)
+            .onDisappear {
+                print("onDisappear 视图消失了!")
+                
+            }.onAppear {
+                print("onAppear 视图出现了!")
+            }.environmentObject(miniData);
     }
     
 }

--
Gitblit v1.9.1