diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 998004a..ce641d0 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -386,15 +386,29 @@ final class PipShadowCoordinator: NSObject, AVPictureInPictureControllerDelegate guard call.method == "shareText", let arguments = call.arguments as? [String: Any], let text = arguments["text"] as? String, - !text.isEmpty, - let controller = self?.window?.rootViewController else { + !text.isEmpty else { result(FlutterError(code: "share_unavailable", message: "Share sheet is unavailable", details: nil)) return } - let activity = UIActivityViewController(activityItems: [text], applicationActivities: nil) - activity.popoverPresentationController?.sourceView = controller.view - activity.completionWithItemsHandler = { _, _, _, _ in result(nil) } - controller.present(activity, animated: true) + DispatchQueue.main.async { + guard let controller = self?.sharePresenter() else { + result(FlutterError(code: "share_unavailable", message: "Share sheet is unavailable", details: nil)) + return + } + let activity = UIActivityViewController(activityItems: [text], applicationActivities: nil) + if let popover = activity.popoverPresentationController { + popover.sourceView = controller.view + popover.sourceRect = CGRect( + x: controller.view.bounds.midX, + y: controller.view.bounds.midY, + width: 0, + height: 0 + ) + popover.permittedArrowDirections = [] + } + activity.completionWithItemsHandler = { _, _, _, _ in result(nil) } + controller.present(activity, animated: true) + } } let thumbnailChannel = FlutterMethodChannel( @@ -459,6 +473,34 @@ final class PipShadowCoordinator: NSObject, AVPictureInPictureControllerDelegate } } + /// 当前前台 Scene 的根控制器之上,最适合展示系统分享面板的控制器。 + private func sharePresenter() -> UIViewController? { + let keyWindow = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first { $0.activationState == .foregroundActive }? + .windows.first { $0.isKeyWindow } + guard let rootController = keyWindow?.rootViewController else { return nil } + let presenter = topPresentedController(from: rootController) + guard presenter.viewIfLoaded?.window != nil else { return nil } + return presenter + } + + /// 解析已展示页面、导航栈和标签页中的当前可见控制器。 + private func topPresentedController(from controller: UIViewController) -> UIViewController { + if let presented = controller.presentedViewController { + return topPresentedController(from: presented) + } + if let navigation = controller as? UINavigationController, + let visible = navigation.visibleViewController { + return topPresentedController(from: visible) + } + if let tabs = controller as? UITabBarController, + let selected = tabs.selectedViewController { + return topPresentedController(from: selected) + } + return controller + } + /// iOS 画中画:影子播放器通道,与 Android 共用同一 channel 名。 private func registerPipChannel(_ engineBridge: FlutterImplicitEngineBridge) { let channel = FlutterMethodChannel( diff --git a/lib/pages/player/widgets/home_feed_overlay.dart b/lib/pages/player/widgets/home_feed_overlay.dart index 4331df2..a85f3b4 100644 --- a/lib/pages/player/widgets/home_feed_overlay.dart +++ b/lib/pages/player/widgets/home_feed_overlay.dart @@ -27,6 +27,7 @@ const double _progressHeight = 2; const double _progressHitHeight = 16; const double _pageHorizontal = 20; const double _railRight = 14; +const double _railShiftRight = 8; const double _railGap = 12; const double _railWidth = 56; const double _infoWatchAllGap = 2; @@ -234,7 +235,10 @@ class _HomeFeedOverlayState extends State { padding: const EdgeInsets.only( bottom: _railBottomOffset, ), - child: _rail(), + child: Transform.translate( + offset: const Offset(_railShiftRight, 0), + child: _rail(), + ), ), ), ], diff --git a/test/home/home_preview_layout_test.dart b/test/home/home_preview_layout_test.dart index 6e94a94..a0429f6 100644 --- a/test/home/home_preview_layout_test.dart +++ b/test/home/home_preview_layout_test.dart @@ -373,7 +373,7 @@ void main() { expect( tester.getRect(find.byKey(const Key('home-feed-action-rail'))).right, closeTo( - tester.getRect(find.byKey(const Key('home-feed-gesture'))).right - 14, + tester.getRect(find.byKey(const Key('home-feed-gesture'))).right - 6, 0.1, ), );