Last active
January 30, 2024 21:49
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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