diff --git a/ios/OralTrainer.xcodeproj/project.xcworkspace/xcuserdata/liushuming.xcuserdatad/UserInterfaceState.xcuserstate b/ios/OralTrainer.xcodeproj/project.xcworkspace/xcuserdata/liushuming.xcuserdatad/UserInterfaceState.xcuserstate
index 0ce9aee..3b3d8e5 100644
Binary files a/ios/OralTrainer.xcodeproj/project.xcworkspace/xcuserdata/liushuming.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/OralTrainer.xcodeproj/project.xcworkspace/xcuserdata/liushuming.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/ios/OralTrainer/AppDelegate.swift b/ios/OralTrainer/AppDelegate.swift
index 5efd84d..ec7ccc7 100644
--- a/ios/OralTrainer/AppDelegate.swift
+++ b/ios/OralTrainer/AppDelegate.swift
@@ -9,4 +9,38 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
) -> UIInterfaceOrientationMask {
AppDelegate.orientationLock
}
+
+ static func lockOrientation(_ mask: UIInterfaceOrientationMask) {
+ orientationLock = mask
+ guard let scene = UIApplication.shared.connectedScenes
+ .compactMap({ $0 as? UIWindowScene })
+ .first(where: { $0.activationState == .foregroundActive })
+ ?? UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).first
+ else {
+ return
+ }
+
+ func update(_ controller: UIViewController?) {
+ guard let controller else { return }
+ controller.setNeedsUpdateOfSupportedInterfaceOrientations()
+ update(controller.presentedViewController)
+ controller.children.forEach { update($0) }
+ }
+
+ let requested: UIInterfaceOrientationMask
+ if mask.contains(.landscapeRight) {
+ requested = .landscapeRight
+ } else if mask.contains(.landscapeLeft) {
+ requested = .landscapeLeft
+ } else {
+ requested = mask
+ }
+
+ scene.windows.forEach { update($0.rootViewController) }
+ scene.requestGeometryUpdate(.iOS(interfaceOrientations: requested)) { _ in }
+ DispatchQueue.main.async {
+ scene.windows.forEach { update($0.rootViewController) }
+ scene.requestGeometryUpdate(.iOS(interfaceOrientations: requested)) { _ in }
+ }
+ }
}
diff --git a/ios/OralTrainer/Resources/Info.plist b/ios/OralTrainer/Resources/Info.plist
index e67e3d5..1cb0a2f 100644
--- a/ios/OralTrainer/Resources/Info.plist
+++ b/ios/OralTrainer/Resources/Info.plist
@@ -33,6 +33,8 @@
需要麦克风权限才能进行录音评测
UILaunchScreen
+ UIRequiresFullScreen
+
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
diff --git a/ios/OralTrainer/Views/FullscreenPlayerView.swift b/ios/OralTrainer/Views/FullscreenPlayerView.swift
index 6ed296e..4ee8ff3 100644
--- a/ios/OralTrainer/Views/FullscreenPlayerView.swift
+++ b/ios/OralTrainer/Views/FullscreenPlayerView.swift
@@ -9,6 +9,29 @@ struct FullscreenPlayerView: View {
@State private var explanationSentence: SentenceBoundary?
var body: some View {
+ GeometryReader { proxy in
+ let fakeLandscape = proxy.size.height > proxy.size.width
+ playerChrome
+ .frame(
+ width: fakeLandscape ? proxy.size.height : proxy.size.width,
+ height: fakeLandscape ? proxy.size.width : proxy.size.height
+ )
+ .rotationEffect(.degrees(fakeLandscape ? -90 : 0))
+ .frame(width: proxy.size.width, height: proxy.size.height)
+ }
+ .background(Color.black)
+ .ignoresSafeArea()
+ .statusBarHidden(true)
+ .sheet(item: $explanationSentence) { sentence in
+ SentenceExplanationSheet(
+ mediaId: controller.currentItem?.id ?? "",
+ courseTitle: controller.currentItem?.title ?? "口语训练课程",
+ sentence: sentence
+ )
+ }
+ }
+
+ private var playerChrome: some View {
ZStack {
Color.black.ignoresSafeArea()
PlayerSurfaceView(controller: controller) { isSwipeUp in
@@ -95,13 +118,6 @@ struct FullscreenPlayerView: View {
}
.padding(16)
}
- .sheet(item: $explanationSentence) { sentence in
- SentenceExplanationSheet(
- mediaId: controller.currentItem?.id ?? "",
- courseTitle: controller.currentItem?.title ?? "口语训练课程",
- sentence: sentence
- )
- }
}
@State private var progressDragging = false
diff --git a/ios/OralTrainer/Views/RootView.swift b/ios/OralTrainer/Views/RootView.swift
index f45305a..ef48f4c 100644
--- a/ios/OralTrainer/Views/RootView.swift
+++ b/ios/OralTrainer/Views/RootView.swift
@@ -115,7 +115,9 @@ struct RootView: View {
FullscreenPlayerView(controller: controller) {
exitFullscreen()
}
- .onAppear(perform: enterFullscreen)
+ .onAppear {
+ AppDelegate.lockOrientation(.landscape)
+ }
}
}
@@ -519,26 +521,14 @@ struct RootView: View {
// MARK: - Fullscreen
private func enterFullscreen() {
- AppDelegate.orientationLock = UIDevice.current.userInterfaceIdiom == .pad ? .all : .landscape
- updateSupportedInterfaceOrientations()
+ AppDelegate.lockOrientation(.landscape)
showFullscreenPlayer = true
}
private func exitFullscreen() {
- AppDelegate.orientationLock = UIDevice.current.userInterfaceIdiom == .pad ? .all : .portrait
- updateSupportedInterfaceOrientations()
+ let restored: UIInterfaceOrientationMask =
+ UIDevice.current.userInterfaceIdiom == .pad ? .all : .portrait
+ AppDelegate.lockOrientation(restored)
showFullscreenPlayer = false
}
-
- private func updateSupportedInterfaceOrientations() {
- let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
- let controllers = scenes.flatMap { $0.windows }.compactMap(\.rootViewController)
- for controller in controllers {
- if #available(iOS 16.0, *) {
- controller.setNeedsUpdateOfSupportedInterfaceOrientations()
- } else {
- UIViewController.attemptRotationToDeviceOrientation()
- }
- }
- }
}