Инициализировать элемент в структуре представления

Я хочу иметь WKWebView с обработкой JavaScript в SwiftUI. Из Инициализация переменных Swift я делаю следующее: (я использую https://github.com/kylehickinson/SwiftUI-WebView, чтобы предоставить оболочку для WKWebView в SwiftUI, которая также добавляет ценные ограничения макета.)

struct ContentView: View {

    var body: some View {
        WebView(webView: myWebView)
        .onAppear {
            let url = Bundle.main.url(forResource: "index", withExtension: "html")!
            myWebView.loadFileURL(url, allowingReadAccessTo: url)
            let request = URLRequest(url: url)
            myWebView.load(request)
        }
    }

    class JSHandler : NSObject, WKScriptMessageHandler {
        var contentView: ContentView?

        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print("documentReady")
            contentView!.myWebView.evaluateJavaScript("<Long Script to Transfer Data>") { (result, error) in
            }
        }
    }

    let myJSHandler = JSHandler()

    var myWebView: WKWebView = {
        let config = WKWebViewConfiguration()
        let controller = WKUserContentController()
        controller.add(myJSHandler , name: "documentReady")
        config.userContentController = controller
        return WKWebView(frame: .zero, configuration: config)
    }()
}

Но потом я узнал от Член экземпляра не может быть использован для типа, что это не работает, потому что замыкание не имеет ссылки на себя. Мне нужно, чтобы WKWebView имел выделенный объект конфигурации, поэтому я не могу просто использовать другой конструктор. Мне нужна ссылка на это, чтобы сделать evaluateJavaScript.

Как заставить это работать?

РЕДАКТИРОВАТЬ 1: Добавьте body и укажите структуру, используемую для переноса WKWebView.

Обновлено еще раз: Добавлен код, чтобы уточнить, что мне нужна двусторонняя связь от WKWebView к собственному приложению через WKScriptMessageHandler (чтобы получать уведомления, когда HTML-документ готов) и от собственного приложения к WKWebView через evaluateJavaScript (для передачи данных после того, как документ HTML готов) .

Оберните WKWebView в UIViewRepresentable, как в stackoverflow.com/a/59790493/12299030.

Asperi 17.12.2020 05:28

@Asperi В структуре WebView вы используете var webview: WKWebView = WKWebView(), но мне нужно использовать конструктор с пользовательской конфигурацией. Не столкнусь ли я тогда с той же проблемой? (Я уже использую github.com/kylehickinson/SwiftUI-WebView для переноса WKWebView в UIViewRepresentable.)

An Hoa 17.12.2020 08:43

Вот альтернатива stackoverflow.com/a/60173992/12299030. Вы можете использовать то, что работает для вас.

Asperi 17.12.2020 08:50

@Asperi Спасибо за решение. Я хотел бы отметить, что, поскольку конфигурация была выполнена внутри оболочки, поэтому, если я хочу создать еще один WKWebView с другим набором методов JS, мне придется сделать другую обертку! Другая проблема заключается в том, что мне нужна ссылка на WKWebView в моем ContentView, чтобы я мог вызвать evaluateJavaScript!

An Hoa 17.12.2020 09:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
352
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Просто сделайте myWebView переменной lazy. Это гарантирует, что jsHandler инициализируется до WebView.

lazy var myWebView: WKWebView = {
    let config = WKWebViewConfiguration()
    let controller = WKUserContentController()
    controller.add(myJSHandler , name: "documentReady")
    config.userContentController = controller
    return WKWebView(frame: .zero, configuration: config)
}()

Это не будет работать для представления SwiftUI, поскольку оно не имеет изменяемой личности.

Asperi 17.12.2020 08:51
Ответ принят как подходящий

Одним из возможных решений является настройка WKWebView в init

struct ContentView: View {
    private var myWebView: WKWebView

    init() {
        let config = WKWebViewConfiguration()
        let controller = WKUserContentController()
        controller.add(myJSHandler , name: "documentReady")
        config.userContentController = controller
        myWebView = WKWebView(frame: .zero, configuration: config)
    }

    var body: some View {
        WebView(webView: myWebView)
        .onAppear {
            let url = Bundle.main.url(forResource: "index", withExtension: "html")!
            myWebView.loadFileURL(url, allowingReadAccessTo: url)
            let request = URLRequest(url: url)
            myWebView.load(request)
        }
    }

    class JSHandler : NSObject, WKScriptMessageHandler {

        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print("documentReady")
            message.webView?.evaluateJavaScript("<Long Script to Transfer Data>") { (result, error) in
            }
        }
    }

    let myJSHandler = JSHandler()

}

Я только что нашел решение, переместив myWebView внутрь JSHandler, но тогда я не знаю, как назвать JSHandler, поскольку он не только обрабатывает обратные вызовы JavaScript, но также служит оболочкой для WKWebView. Это намного лучше. Кстати, спасибо за совет использовать message.webView? вместо ссылки на ContentView.

An Hoa 17.12.2020 13:37

Вы просто не можете использовать ссылку на ContentView, потому что это не ссылочный тип, это будет копия.

Asperi 17.12.2020 13:42

Я не знал об этом. Я подумал, что добавление var contentView: ContentView? в JSHandler, как я изначально сделал, работает, поскольку ContentView? может быть nil.

An Hoa 18.12.2020 03:10

Другие вопросы по теме