Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ All notable changes to this project will be documented in this file. Take a look

* The LCP License Document is now accessible via `publication.lcpLicense?.license`, even if the license validation fails with a status error or missing passphrase. This is useful for checking the end date of an expired license or renew a license.

### Fixed

#### Navigator

* The safe area insets strategy was adjusted to take into account changes in iOS/iPadOS 26.


## [3.4.0]

Expand Down
7 changes: 4 additions & 3 deletions Sources/Navigator/EPUB/EPUBFixedSpreadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ final class EPUBFixedSpreadView: EPUBSpreadView {
return
}

// Insets the bounds by the notch area (eg. iPhone X) to make sure that
// the content is not overlapped by the screen notch.
var insets = notchAreaInsets
// We use the window's safeAreaInsets instead of the view's because we
// only want to take into account the device notch and status bar, not
// the application's bars.
var insets = window?.safeAreaInsets ?? UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

// Use the same insets on the left and right side (the largest one) to
// keep the pages centered on the screen even if the notches are not
Expand Down
14 changes: 10 additions & 4 deletions Sources/Navigator/EPUB/EPUBReflowableSpreadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,15 @@ final class EPUBReflowableSpreadView: EPUBSpreadView {
}

private func updateContentInset() {
// We use the window's safeAreaInsets instead of the view's because we
// only want to take into account the device notch and status bar, not
// the application's bars.
let safeAreaInsets = window?.safeAreaInsets ?? UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

if viewModel.scroll {
topConstraint.constant = 0
bottomConstraint.constant = 0
scrollView.contentInset = UIEdgeInsets(top: notchAreaInsets.top, left: 0, bottom: notchAreaInsets.bottom, right: 0)
scrollView.contentInset = UIEdgeInsets(top: safeAreaInsets.top, left: 0, bottom: safeAreaInsets.bottom, right: 0)

} else {
let contentInset = viewModel.config.contentInset
Expand All @@ -98,9 +103,10 @@ final class EPUBReflowableSpreadView: EPUBSpreadView {
?? contentInset[.unspecified]
?? (top: 0, bottom: 0)

// Increases the insets by the notch area (eg. iPhone X) to make sure that the content is not overlapped by the screen notch.
insets.top += notchAreaInsets.top
insets.bottom += notchAreaInsets.bottom
// Increases the insets by the window's safe area insets area to
// make sure that the content is not overlapped by the screen notch.
insets.top += safeAreaInsets.top
insets.bottom += safeAreaInsets.bottom

topConstraint.constant = insets.top
bottomConstraint.constant = -insets.bottom
Expand Down
10 changes: 7 additions & 3 deletions Sources/Navigator/PDF/PDFDocumentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,13 @@ public final class PDFDocumentView: PDFView {
}

private func updateContentInset() {
// Setting the horizontal values triggers shifts the content incorrectly, somehow.
firstScrollView?.contentInset.top = notchAreaInsets.top
firstScrollView?.contentInset.bottom = notchAreaInsets.bottom
// We use the window's safeAreaInsets instead of the view's because we
// only want to take into account the device notch and status bar, not
// the application's bars.
let insets = window?.safeAreaInsets ?? UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

firstScrollView?.contentInset.top = insets.top
firstScrollView?.contentInset.bottom = insets.bottom
}

override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
Expand Down
37 changes: 0 additions & 37 deletions Sources/Navigator/Toolkit/Extensions/UIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,6 @@ import Foundation
import UIKit

extension UIView {
/// Returns the safe area insets taking only into account the device screen notches (eg. on
/// iPhone X), ignoring any UX safe area insets (eg. status bar, navigation bar).
///
/// This can be used to layout the content in a way that makes sure it's not under the physical
/// notches, but at the same time is under the status and navigation bars (which is usually what
/// we want for a reader app).
///
/// We use that instead of pinning the content directly to the safe area layout guides to avoid
/// the view shifting when the status bar is toggled.
var notchAreaInsets: UIEdgeInsets {
guard let window = window else {
return safeAreaInsets
}

var windowSafeAreaInsets = window.safeAreaInsets

// Trick to ignore the status bar on devices without notches (pre iPhone X).
// Notch height is usually at least 44pts tall.
let statusBarSize = window.windowScene?.statusBarManager?.statusBarFrame.size ?? .zero
// The frame is in the coordinate space of the window, so it might be swapped in landscape.
let statusBarHeight = min(statusBarSize.width, statusBarSize.height)
if statusBarHeight < 44, windowSafeAreaInsets.top == statusBarHeight {
windowSafeAreaInsets.top = 0
}

// We take the smallest value between the view's safeAreaInsets and the window's
// safeAreaInsets in case the view is not pinned to the screen edges. In which case, its
// safeAreaInsets will likely be empty and we don't want to take into account the screen
// notch.
return UIEdgeInsets(
top: min(windowSafeAreaInsets.top, safeAreaInsets.top),
left: min(windowSafeAreaInsets.left, safeAreaInsets.left),
bottom: min(windowSafeAreaInsets.bottom, safeAreaInsets.bottom),
right: min(windowSafeAreaInsets.right, safeAreaInsets.right)
)
}

// Finds the first `UIScrollView` in the view hierarchy.
//
// https://medium.com/@wailord/the-particulars-of-the-safe-area-and-contentinsetadjustmentbehavior-in-ios-11-9b842018eeaa#077b
Expand Down
Loading