Skip to content

Instantly share code, notes, and snippets.

@Amzd
Last active November 14, 2024 10:01
Show Gist options
  • Save Amzd/01e1f69ecbc4c82c8586dcd292b1d30d to your computer and use it in GitHub Desktop.
Save Amzd/01e1f69ecbc4c82c8586dcd292b1d30d to your computer and use it in GitHub Desktop.
PreferenceUIHostingController. Adds hiding home indicator and deferring system edge gestures to SwiftUI. (Don't work at the same time but I think that's normal?)
extension View {
/// Controls the application's preferred home indicator auto-hiding when this view is shown.
func prefersHomeIndicatorAutoHidden(_ value: Bool) -> some View {
preference(key: PreferenceUIHostingController.PrefersHomeIndicatorAutoHiddenPreferenceKey.self, value: value)
}
/// Controls the application's preferred screen edges deferring system gestures when this view is shown. Default is UIRectEdgeNone.
func edgesDeferringSystemGestures(_ edge: UIRectEdge) -> some View {
preference(key: PreferenceUIHostingController.PreferredScreenEdgesDeferringSystemGesturesPreferenceKey.self, value: edge)
}
}
class PreferenceUIHostingController: UIHostingController<AnyView> {
init<V: View>(wrappedView: V) {
weak var weakSelf: PreferenceUIHostingController?
super.init(rootView: AnyView(wrappedView
.onPreferenceChange(PrefersHomeIndicatorAutoHiddenPreferenceKey.self) {
weakSelf?._prefersHomeIndicatorAutoHidden = $0
}
.onPreferenceChange(PreferredScreenEdgesDeferringSystemGesturesPreferenceKey.self) {
weakSelf?._preferredScreenEdgesDeferringSystemGestures = $0
}
))
weakSelf = self
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: Prefers Home Indicator Auto Hidden
fileprivate struct PrefersHomeIndicatorAutoHiddenPreferenceKey: PreferenceKey {
typealias Value = Bool
static var defaultValue: Value = false
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue() || value
}
}
private var _prefersHomeIndicatorAutoHidden = false {
didSet { setNeedsUpdateOfHomeIndicatorAutoHidden() }
}
override var prefersHomeIndicatorAutoHidden: Bool {
_prefersHomeIndicatorAutoHidden
}
// MARK: Preferred Screen Edges Deferring SystemGestures
fileprivate struct PreferredScreenEdgesDeferringSystemGesturesPreferenceKey: PreferenceKey {
typealias Value = UIRectEdge
static var defaultValue: Value = []
static func reduce(value: inout Value, nextValue: () -> Value) {
value.formUnion(nextValue())
}
}
private var _preferredScreenEdgesDeferringSystemGestures: UIRectEdge = [] {
didSet { setNeedsUpdateOfScreenEdgesDeferringSystemGestures() }
}
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
_preferredScreenEdgesDeferringSystemGestures
}
}
/// If you are unable to access window.rootViewController this is a method using swizzling
struct PreferenceUIHostingControllerView<Wrapped: View>: UIViewControllerRepresentable {
init(@ViewBuilder wrappedView: @escaping () -> Wrapped) {
_ = UIViewController.preferenceSwizzling
self.wrappedView = wrappedView
}
var wrappedView: () -> Wrapped
func makeUIViewController(context: Context) -> PreferenceUIHostingController {
PreferenceUIHostingController(wrappedView: wrappedView())
}
func updateUIViewController(_ uiViewController: PreferenceUIHostingController, context: Context) {}
}
import SwizzleSwift // I have a fork of this for SPM (Amzd/SwizzleSwift)
extension UIViewController {
static var preferenceSwizzling: Void = {
Swizzle(UIViewController.self) {
#selector(getter: childForScreenEdgesDeferringSystemGestures) <-> #selector(childForScreenEdgesDeferringSystemGestures_Amzd)
#selector(getter: childForHomeIndicatorAutoHidden) <-> #selector(childForHomeIndicatorAutoHidden_Amzd)
}
}()
}
extension UIViewController {
@objc func childForScreenEdgesDeferringSystemGestures_Amzd() -> UIViewController? {
if self is PreferenceUIHostingController {
// dont continue searching
return nil
} else {
return search()
}
}
@objc func childForHomeIndicatorAutoHidden_Amzd() -> UIViewController? {
if self is PreferenceUIHostingController {
// dont continue searching
return nil
} else {
return search()
}
}
private func search() -> PreferenceUIHostingController? {
if let result = children.compactMap({ $0 as? PreferenceUIHostingController }).first {
return result
}
for child in children {
if let result = child.search() {
return result
}
}
return nil
}
}
@LePips
Copy link

LePips commented Dec 4, 2022

If you mean trying to hide the home indicator on ContentView, that does work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment