diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d59aa4..721ffc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,3 +29,4 @@ 2026-11-06 iOS: Update Download on downloaded playlists, albums, artists and stem playlists (their menus and the Downloads tab), plus Update All Downloads. Pulls the current song list, downloads new songs, drops removed ones, retries failed ones. Downloads still sync on their own when the app opens. 2026-11-06 iOS: crossfade, Off or 1-12 s in Settings > Listening. Equal-power fade, Sweet Fades skips it between songs of the same album. Seeking during a fade finishes it. 2026-11-09 iOS: iPhone Duo unfolded uses both sides of the hinge: library on one side, Now Playing docked on the other (no mini player while split). Folded and other iPhones unchanged. 1.0 RC 3 (build 23). +2026-11-09 iOS: Duo split now engages with the phone lying flat (hinge region reports inactive then), with a screen-size fallback if no region is reported. 1.0 RC 4 (build 24). diff --git a/ios/Stemify/UI/RootView.swift b/ios/Stemify/UI/RootView.swift index 34684b1..6a03650 100644 --- a/ios/Stemify/UI/RootView.swift +++ b/ios/Stemify/UI/RootView.swift @@ -1,4 +1,5 @@ import SwiftUI +import UIKit /// The tab bar, a navigation stack per tab, the mini player and the Now Playing sheet. /// On an unfolded iPhone Duo the library sits on one side of the hinge and Now Playing on the other. @@ -171,13 +172,23 @@ private struct DuoSplit: View { private struct Cut { var vertical: Bool; var first: CGFloat; var gap: CGFloat; var second: CGFloat } private static func cut(_ geo: GeometryProxy) -> Cut? { - guard #available(iOS 27.1, *), - let r = geo.reservedRegions(kind: .division).first(where: \.isActive)?.frame else { return nil } let size = geo.size - let vertical = r.height >= r.width - let c = vertical - ? Cut(vertical: true, first: r.minX, gap: r.width, second: size.width - r.maxX) - : Cut(vertical: false, first: r.minY, gap: r.height, second: size.height - r.maxY) + var c: Cut? + // The hinge reports inactive while the phone lies flat, so ask for inactive regions too. + if #available(iOS 27.1, *), + let r = geo.reservedRegions(kind: .division, options: .includeInactive).first?.frame { + let vertical = r.height >= r.width + c = vertical + ? Cut(vertical: true, first: r.minX, gap: r.width, second: size.width - r.maxX) + : Cut(vertical: false, first: r.minY, gap: r.height, second: size.height - r.maxY) + } else if UIDevice.current.userInterfaceIdiom == .phone, size.width >= 760, size.height >= 560 { + // No region reported: only an unfolded Duo gives an iPhone this much room both ways. + // The hinge sits at the middle of the screen, not of the safe area, so place it from there. + let screenW = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.screen.bounds.width + let half = ((screenW ?? size.width) / 2 - geo.frame(in: .global).minX).rounded() + c = Cut(vertical: true, first: half - 12, gap: 24, second: size.width - half - 12) + } + guard let c else { return nil } // A region that doesn't leave usable room on both sides isn't a hinge worth splitting at. return c.first > 200 && c.second > 200 ? c : nil } diff --git a/ios/project.yml b/ios/project.yml index 35de7b6..583c7bf 100644 --- a/ios/project.yml +++ b/ios/project.yml @@ -8,8 +8,8 @@ settings: SWIFT_VERSION: "5.0" # iOS needs a plain number for the bundle version; the label is what the app and releases show. MARKETING_VERSION: "1.0" - VERSION_LABEL: "1.0 RC 3" - CURRENT_PROJECT_VERSION: "23" + VERSION_LABEL: "1.0 RC 4" + CURRENT_PROJECT_VERSION: "24" targets: Stemify: type: application