Skip to content

Instantly share code, notes, and snippets.

@drewolbrich
Last active January 30, 2024 21:49
Show Gist options
  • Save drewolbrich/90f11b7170267674cbdcb4a0d71ce873 to your computer and use it in GitHub Desktop.
Save drewolbrich/90f11b7170267674cbdcb4a0d71ce873 to your computer and use it in GitHub Desktop.
A view modifier that sets the resizing restrictions for the view's window
// IMPORTANT: The following code doesn't support specifying a window's
// minimum and maximum size when used to constrain a window's aspect ratio.
// Please use this newer view modifier extension instead:
// https://gist.github.com/drewolbrich/03460fc1bb71b9a821fff722f17ec977
//
// View+WindowResizingRestrictions.swift
//
// Created by Drew Olbrich on 11/19/23.
//
import SwiftUI
extension View {
/// Sets the resizing restrictions for the view's window.
///
/// The `resizingRestrictions` parameter may have one of the following values:
///
/// - **none**: Do not display the window's resize handle when the user gazes at the window's lower left and right corners. The window will not be resizable.
/// - **uniform**: Allow the user to resize the window, but constrain the window's aspect ratio to match the values passed to the containing window group's `defaultSize` view modifier.
/// - **freeform**: Allow the user to freely resize the window. This is the default behavior.
///
/// This view modifier was written for visionOS 1.0 beta 6, and may or may not work
/// as expected in future visionOS beta releases.
///
/// ## Example Usage
///
/// ```
/// WindowGroup {
/// MyView()
/// // Do not display the window's resize handle.
/// .windowResizingRestrictions(.none)
/// }
/// .defaultSize(width: 600, height: 400)
///
/// WindowGroup {
/// MyView()
/// // Constrain the window's size to a fixed aspect ratio.
/// .windowResizingRestrictions(.uniform)
/// }
/// .defaultSize(width: 500, height: 500)
/// ```
///
/// - Parameter resizingRestrictions: The resizing restrictions to assign to the view's window.
/// - Returns: A view whose window has the specified resizing restrictions assigned.
func windowResizingRestrictions(_ resizingRestrictions: UIWindowScene.ResizingRestrictions) -> some View {
modifier(WindowResizingRestrictionsViewModifier(resizingRestrictions: resizingRestrictions))
}
}
private struct WindowResizingRestrictionsViewModifier: ViewModifier {
let resizingRestrictions: UIWindowScene.ResizingRestrictions
func body(content: Content) -> some View {
WindowResizingRestrictionsView(resizingRestrictions: resizingRestrictions, content: {
content
})
}
}
private struct WindowResizingRestrictionsView<Content>: UIViewControllerRepresentable where Content: View {
let resizingRestrictions: UIWindowScene.ResizingRestrictions
let content: () -> Content
func makeUIViewController(context: Context) -> WindowResizingRestrictionsUIViewController<Content> {
WindowResizingRestrictionsUIViewController(resizingRestrictions: resizingRestrictions, content: content)
}
func updateUIViewController(_ uiViewController: WindowResizingRestrictionsUIViewController<Content>, context: Context) {
// Do nothing.
}
}
private class WindowResizingRestrictionsUIViewController<Content>: UIViewController where Content: View {
private let resizingRestrictions: UIWindowScene.ResizingRestrictions
private let hostingController: UIHostingController<Content>
init(resizingRestrictions: UIWindowScene.ResizingRestrictions, content: @escaping () -> Content) {
self.resizingRestrictions = resizingRestrictions
self.hostingController = UIHostingController(rootView: content())
super.init(nibName: nil, bundle: nil)
addChild(hostingController)
hostingController.view.frame = view.bounds
view.addSubview(hostingController.view)
hostingController.didMove(toParent: self)
hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = WindowResizingRestrictionsUIView(resizingRestrictions: resizingRestrictions)
}
}
private class WindowResizingRestrictionsUIView: UIView {
private let resizingRestrictions: UIWindowScene.ResizingRestrictions
init(resizingRestrictions: UIWindowScene.ResizingRestrictions) {
self.resizingRestrictions = resizingRestrictions
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToWindow() {
window?.windowScene?.requestGeometryUpdate(.Vision(resizingRestrictions: resizingRestrictions))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment