Есть ли способ в Swift, чтобы частично соответствовать generi c? - PullRequest
2 голосов
/ 25 февраля 2020

То есть, если у меня есть класс 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», но это хрупко по двум причинам:

  1. Если я изменю модификаторы, компилятор не предупредит меня, что мне нужно обновить приведение, и
  2. Поскольку «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 в качестве типа контента.

Любые идеи приветствуются.

...