Rework corner layout with portrait-style info panel below the video

The corner layout now always renders the info page as a portrait-style
panel below the video (collapsible description, drag to hide/expand),
while the side column holds tabs for the remaining pages. Key pieces:

- Below-video panel reuses PortraitDetailsPanel (no comments pill,
  square corners, chapters/related stay in the side column tabs)
- Drag down hides the panel with the video recentering in lockstep;
  swipe up on the video reveals it following the finger
- Fullscreen button toggles the panels in corner layout (controls hide
  first so their resize never plays visibly); status bar and home
  indicator hide only while the panels are hidden
- Panel visibility and column width are seeded at init from a warmed
  PlayerControlsLayout cache, so the first frame resolves corner mode
  without flashing the default layout or animating the panels in
- Dismiss pan is blocked inside the below-video panel frame
  (cornerBelowPanelFrame in NavigationCoordinator)
- Resize grabber slimmed to match the panel drag handle look
This commit is contained in:
Arkadiusz Fal
2026-09-23 07:25:02 +02:00
parent ec13fda115
commit 05a03723f6
14 changed files with 586 additions and 435 deletions

View File

@@ -144,6 +144,8 @@ private final class DragToDismissGestureHandler: NSObject, UIGestureRecognizerDe
var isPanelDragging: (() -> Bool)?
/// Returns the portrait panel frame in screen coordinates (for gesture conflict resolution)
var getPortraitPanelFrame: (() -> CGRect)?
/// Returns the corner layout below-video panel frame in screen coordinates (blocks dismiss pan)
var getCornerBelowPanelFrame: (() -> CGRect)?
/// Returns whether the portrait panel is currently visible (not hidden off-screen)
var isPortraitPanelVisible: (() -> Bool)?
/// Returns the progress bar frame in screen coordinates (for gesture conflict resolution)
@@ -366,6 +368,16 @@ private final class DragToDismissGestureHandler: NSObject, UIGestureRecognizerDe
return false
}
// Corner layout: don't begin dismiss gesture from inside the
// below-video panel — its drag handle hides the panel instead
let belowPanelFrame = getCornerBelowPanelFrame?() ?? .zero
if !belowPanelFrame.isEmpty {
let touchLocation = panGesture.location(in: nil)
if belowPanelFrame.contains(touchLocation) {
return false
}
}
// When portrait panel is visible, only allow dismiss from player area (above panel)
// This prevents race conditions where isPanelDragging isn't set yet when gesture begins
let panelFrame = getPortraitPanelFrame?() ?? .zero
@@ -845,6 +857,9 @@ final class ExpandedPlayerWindowManager {
handler.getPortraitPanelFrame = { [weak self] in
self?.appEnvironment?.navigationCoordinator.portraitPanelFrame ?? .zero
}
handler.getCornerBelowPanelFrame = { [weak self] in
self?.appEnvironment?.navigationCoordinator.cornerBelowPanelFrame ?? .zero
}
handler.isPortraitPanelVisible = { [weak self] in
self?.appEnvironment?.navigationCoordinator.isPortraitPanelVisible ?? true
}