Интересно, есть ли у кого-нибудь хорошие решения для создания в Swift объекта пользовательского интерфейса, похожего на выдвижной ящик, который будет иметь половину ширины экрана в альбомной ориентации и привязан к переднему краю экрана. В книжной ориентации ширина должна быть равна ширине экрана.
У меня есть код, который использует UISheetPresentationController для создания листа и обновления его ширины при повороте устройства. Интересно, смогу ли я тоже установить положение листа. По умолчанию оно центрировано.
import UIKit
// MARK: - SheetNavigationController
/// A `UINavigationController` subclass that automatically handles sheet presentation detents and sizing during device orientation changes.
class SheetNavigationController: UINavigationController, UISheetPresentationControllerDelegate {
/// A closure to be executed when the sheet is dismissed.
var onDismiss: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
configureSheetPresentation()
// Set the delegate to self to handle dismissal.
sheetPresentationController?.delegate = self
NotificationCenter.default.addObserver(self,
selector: #selector(orientationDidChange),
name: UIDevice.orientationDidChangeNotification,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: - UISheetPresentationControllerDelegate
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
onDismiss?()
}
@objc private func orientationDidChange() {
// A small delay is necessary to ensure the orientation change has completed before re-calculating the sheet's properties.
DispatchQueue.main.async {
self.configureSheetPresentation()
}
}
/// Configures the sheet presentation properties, including detents and preferred content size, based on the current device idiom and orientation.
private func configureSheetPresentation() {
guard let sheet = sheetPresentationController else { return }
// iPad configuration
if UIDevice.current.userInterfaceIdiom == .pad {
sheet.detents = [.medium(), .large()]
return
}
// iPhone configuration
let isLandscape = view.window?.windowScene?.interfaceOrientation.isLandscape ?? (view.bounds.width > view.bounds.height)
// Size
let widthScaleFactor: CGFloat = isLandscape ? 0.5 : 1.0
preferredContentSize = CGSize(width: widthScaleFactor * UIScreen.main.bounds.width, height: 0)
// Detents
let heightScaleFactor = isLandscape ? 0.75 : 0.35
let initialDetent = UISheetPresentationController.Detent.custom(identifier: .init("initial")) { _ in
heightScaleFactor * UIScreen.main.bounds.height
}
var detents = [initialDetent]
if !isLandscape {
detents.append(.large())
}
sheet.detents = detents
sheet.selectedDetentIdentifier = initialDetent.identifier
}
}
// MARK: - UIViewController Extension
extension UIViewController {
/// Presents a view controller as a sheet that automatically handles orientation changes.
///
/// - Parameters:
/// - rootViewController: The root view controller for the sheet's navigation stack.
/// - onDismiss: A closure to execute when the sheet is dismissed by the user.
func presentAsSheet(_ rootViewController: UIViewController, onDismiss: (() -> Void)? = nil) {
let navController = SheetNavigationController(rootViewController: rootViewController)
navController.onDismiss = onDismiss
if UIDevice.current.userInterfaceIdiom == .pad {
navController.modalPresentationStyle = .pageSheet
} else {
navController.modalPresentationStyle = .formSheet
}
if let sheet = navController.sheetPresentationController {
sheet.prefersGrabberVisible = true
sheet.largestUndimmedDetentIdentifier = .large
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.prefersEdgeAttachedInCompactHeight = true
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
}
present(navController, animated: true)
}
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... -landscape