То есть, если у меня есть класс C, который принимает два родовых объекта A и B, есть ли способ, где я могу привести объект к C, где мне все равно, что такое B?
Мой конкретный c вариант использования заключается в том, что мне нужно соединить функциональность NSView с новым SwiftUI в многооконном, но не основанном на документе приложении. Проблема, с которой я сталкиваюсь, заключается в том, что, учитывая NSView, мне нужно получить SwiftUI View, которым он управляет (в моем случае View называется ContentView
).
Обратите внимание, что у меня есть решение, которое я включите ниже, но это включает использование отражения на основе Mirror
, и мне интересно, есть ли лучший способ, скорее всего, с использованием as?
для приведения к частичному совпадению обобщенного c.
Мостовое соединение выполняется с использованием NSHostingView, поэтому может показаться, что можно просто сделать следующее:
if let hostingView = NSApplication.shared.keyWindow?.contentView as? NSHostingView<ContentView> {
// do what I need with 'hostingView.rootView'
}
К сожалению, NSHostingView.rootView не возвращает фактический ContentView
, который я создал, он возвращает модифицированная версия этого представления зависит от используемых модификаторов. (В моем случае я использую модификатор .environmentObject
.) В результате вышеприведенный оператор if
никогда не возвращает true, потому что тип не NSHostingView<ContentView>
, а скорее NSHostingView<ModifiedContent<ContentView, _bunch_Of_Gobbletygook_Representing_The_Modifiers>>
. Один из способов «решить» проблему - распечатать результат type(of: hostingView)
, когда я создаю окно, а затем изменить свой состав, чтобы включить текущую версию «gobbledygook», но это хрупко по двум причинам:
- Если я изменю модификаторы, компилятор не предупредит меня, что мне нужно обновить приведение, и
- Поскольку «gobbledygook» содержит одиночные подчеркнутые значения, я должен принять те внутренние детали, которые могут измениться. Следовательно, без изменения какого-либо кода обновление ОС может вызвать сбой приведения.
Поэтому я создал решение в виде следующего расширения NSView:
extension NSView {
func originalRootView<RootView: View>() -> RootView? {
if let hostingView = self as? NSHostingView<RootView> {
return hostingView.rootView
}
let mirror = Mirror(reflecting: self)
if let rootView = mirror.descendant("_rootView") {
let mirror2 = Mirror(reflecting: rootView)
if let content = mirror2.descendant("content") as? RootView {
return content
}
}
return nil
}
}
Это позволяет мне справляться со своими потребностями, используя следующее:
private func currentContentView() -> ContentView? {
return NSApplication.shared.keyWindow?.contentView?.originalRootView()
}
... sometime later ...
if let contentView = currentContentView() {
// do what I need with contentView
}
Я хотел бы знать, есть ли способ реализовать originalRootView
без использования отражения, предположительно, путем разрешения частично определенного приведение к объекту ModifiedContent
. Например, что-то вроде следующего (который не компилируется):
extension NSView {
func originalRootView<RootView: View>() -> RootView? {
if let hostingView = self as? NSHostingView<RootView> {
return hostingView.rootView
}
if let hostingView = self as? NSHostingView<ModifiedContent<RootView, ANY>> {
return hostingView.rootView.content
}
return nil
}
}
Проблема в том, что поставить для «ЛЮБОГО». Я бы подумал, что какая-то форма Any
или AnyObject
, но комплимент жалуется на это. По сути, я хотел бы сказать компилятору, что мне все равно, что ЛЮБОЕ, если у ModifiedContent есть RootView в качестве типа контента.
Любые идеи приветствуются.