If you want support multiple screens, and use frame tags or Popovers.popover(tagged:), you need to specify the window scene. The problem is, getting a view's window scene in SwiftUI is weird.
My current workaround (involves 0.5 second hardcoded delay):
|
/// Get the parent window from a view. Help needed! From https://stackoverflow.com/a/63276688/14351818 |
|
struct WindowSceneReader: UIViewRepresentable { |
|
|
|
/// A closure that's called when the window is found. |
|
var found: ((UIWindowScene?) -> Void) |
|
|
|
func makeUIView(context: Context) -> UIView { |
|
let view = UIView() |
|
|
|
/** |
|
The 0.5 second delay is needed to wait until the window is first initialized. |
|
However, it's hardcoded. Does anyone know how to work around this? |
|
*/ |
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak view] in |
|
if let windowScene = view?.window?.windowScene { |
|
found(windowScene) |
|
} |
|
} |
|
|
|
return view |
|
} |
|
|
|
func updateUIView(_ uiView: UIView, context: Context) { } |
|
} |
|
|
|
/** |
|
Read the window scene from a view and inject it as an environment object. iOS 14+ due to `@StateObject`. |
|
*/ |
|
@available(iOS 14, *) |
|
public struct WindowSceneInjectorModifier: ViewModifier { |
|
|
|
/// This is only iOS 14+. Looking for help in making this available for iOS 13. |
|
@StateObject var windowSceneModel = WindowSceneModel() |
|
|
|
public func body(content: Content) -> some View { |
|
content |
|
|
|
/// Inject the window into the subview. |
|
.environmentObject(windowSceneModel) |
|
|
|
/// Read the window. |
|
.background( |
|
WindowSceneReader { scene in |
|
windowSceneModel.windowScene = scene |
|
} |
|
) |
|
} |
|
} |
You can then use this in your app's root view via injectWindowScene.
|
/** |
|
Get a view's window and make it accessible to all subviews. iOS 14+ due to `@StateObject` - looking for help in making it available for iOS 13. |
|
|
|
Only necessary if your app supports multiple windows. |
|
*/ |
|
@available(iOS 14, *) |
|
func injectWindowScene() -> some View { |
|
return self.modifier(WindowSceneInjectorModifier()) |
|
} |
WindowGroup {
ContentView()
.injectWindowScene() /// Make the window scene available to all subviews. Not ideal, but it works (usually).
}
struct ContentView: View {
@EnvironmentObject var windowSceneModel: WindowSceneModel
/// ...
Text("Hello").frameTag("Your Frame Tag Name", in: windowSceneModel.windowScene)
}
The hardcoded delay especially is annoying. Typing out @EnvironmentObject var windowSceneModel: WindowSceneModel every time you want to access the window scene is ok, but it's still kind of long.
Does anyone know how to get a view's window in SwiftUI? Preferably, something like this:
struct ContentView: View {
var body: some View {
Text("Hello")
.frameTag("Your Frame Tag Name")
}
}
public extension View {
func frameTag(_ tag: String) -> some View {
let windowScene = theWindowSceneThatHostsThisView /// Something like this? So no more need to specify the window scene manually.
return self.modifier(FrameTagModifier(tag: tag, windowScene: windowScene))
}
}
If you want support multiple screens, and use frame tags or
Popovers.popover(tagged:), you need to specify the window scene. The problem is, getting a view's window scene in SwiftUI is weird.My current workaround (involves 0.5 second hardcoded delay):
Popovers/Sources/PopoverFrameTag.swift
Lines 67 to 114 in 5fcaa9d
You can then use this in your app's root view via
injectWindowScene.Popovers/Sources/PopoverFrameTag.swift
Lines 130 to 138 in 8e22ac7
The hardcoded delay especially is annoying. Typing out
@EnvironmentObject var windowSceneModel: WindowSceneModelevery time you want to access the window scene is ok, but it's still kind of long.Does anyone know how to get a view's window in SwiftUI? Preferably, something like this: