diff --git a/apps/ios/CrossDeskMobile/RemoteSessionModel.swift b/apps/ios/CrossDeskMobile/RemoteSessionModel.swift index ebc7aabf..0039f783 100644 --- a/apps/ios/CrossDeskMobile/RemoteSessionModel.swift +++ b/apps/ios/CrossDeskMobile/RemoteSessionModel.swift @@ -948,7 +948,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe return "连接建立后可调整,需被控端支持。" } if videoSettings.failed { return "调整失败,请重试。" } - return videoSettings.pending ? "正在应用…" : "" + return "" } func updateVideoSettings(quality: Int? = nil, frameRate: Int? = nil, diff --git a/apps/ios/CrossDeskMobile/RemoteSessionView.swift b/apps/ios/CrossDeskMobile/RemoteSessionView.swift index 80e87645..0feda373 100644 --- a/apps/ios/CrossDeskMobile/RemoteSessionView.swift +++ b/apps/ios/CrossDeskMobile/RemoteSessionView.swift @@ -279,7 +279,7 @@ struct RemoteSessionView: View { containerSize: containerSize ) - ZStack { + ZStack(alignment: .topLeading) { if statusMenuVisible { Color.clear .contentShape(Rectangle()) @@ -288,36 +288,45 @@ struct RemoteSessionView: View { statusMenuVisible = false } } - - FloatingSessionMenu( - session: session, - showKeyboard: { - statusMenuVisible = false - keyboardInputVisible.toggle() - }, - chooseFile: { - statusMenuVisible = false - showingFileImporter = true - }, - close: { - withAnimation(.easeOut(duration: 0.14)) { - statusMenuVisible = false - } - }, - disconnect: { - statusMenuVisible = false - keyboardInputVisible = false - withAnimation(.easeOut(duration: 0.18)) { - disconnectConfirmationVisible = true - } - } - ) - .frame(width: panelSize.width, height: panelSize.height) - .position(panelCenter) - .transition(.scale(scale: 0.94, anchor: .topTrailing) - .combined(with: .opacity)) } + // Position a stable container, then animate only the panel + // inside it. Scaling a view after .position also scales its + // full-screen positioning space and makes the panel drift. + ZStack { + if statusMenuVisible { + FloatingSessionMenu( + session: session, + showKeyboard: { + statusMenuVisible = false + keyboardInputVisible.toggle() + }, + chooseFile: { + statusMenuVisible = false + showingFileImporter = true + }, + close: { + withAnimation(.easeOut(duration: 0.14)) { + statusMenuVisible = false + } + }, + disconnect: { + statusMenuVisible = false + keyboardInputVisible = false + withAnimation(.easeOut(duration: 0.18)) { + disconnectConfirmationVisible = true + } + } + ) + .frame(width: panelSize.width, height: panelSize.height) + .transition(.scale(scale: 0.94, anchor: .topTrailing) + .combined(with: .opacity)) + } + } + .frame(width: panelSize.width, height: panelSize.height) + .position(panelCenter) + .allowsHitTesting(statusMenuVisible) + CrossDeskStatusOrb(isExpanded: statusMenuVisible) .frame(width: 54, height: 54) .contentShape(Circle()) @@ -347,6 +356,7 @@ struct RemoteSessionView: View { } ) } + .frame(width: containerSize.width, height: containerSize.height) } } @@ -1071,8 +1081,8 @@ private struct FloatingSessionMenu: View { @ObservedObject var session: RemoteSessionModel private enum Page { case controls, videoSettings, networkStats } @State private var page: Page = .controls - @State private var displayedNetworkSnapshot = RemoteNetworkSnapshot() - @State private var displayedFrameSize = CGSize.zero + @State private var displayedNetworkSnapshot: RemoteNetworkSnapshot + @State private var displayedFrameSize: CGSize let showKeyboard: () -> Void let chooseFile: () -> Void let close: () -> Void @@ -1081,6 +1091,22 @@ private struct FloatingSessionMenu: View { private let columns = Array(repeating: GridItem(.flexible(), spacing: 7), count: 3) + init(session: RemoteSessionModel, + showKeyboard: @escaping () -> Void, + chooseFile: @escaping () -> Void, + close: @escaping () -> Void, + disconnect: @escaping () -> Void) { + self.session = session + self.showKeyboard = showKeyboard + self.chooseFile = chooseFile + self.close = close + self.disconnect = disconnect + // Render the first frame with real values instead of replacing empty + // placeholders while the panel's insertion animation is running. + _displayedNetworkSnapshot = State(initialValue: session.networkSnapshot) + _displayedFrameSize = State(initialValue: session.frameSize) + } + var body: some View { VStack(spacing: 9) { HStack(spacing: 8) { @@ -1102,9 +1128,11 @@ private struct FloatingSessionMenu: View { VStack(alignment: .leading, spacing: 1) { Text(session.connectionStatus) .font(.subheadline.weight(.semibold)) + .lineLimit(1) Text(statusDetail) .font(.caption2.monospacedDigit()) .foregroundStyle(.secondary) + .lineLimit(1) } } Spacer()