WKWebView en Interface Builder

95

Parece que las plantillas de objetos IB en XCode 6 beta todavía están creando objetos de estilo antiguo (UIWebView para iOS y WebView para OSX). Con suerte, Apple los actualizará para el WebKit moderno, pero hasta entonces, ¿cuál es la mejor manera de crear WKWebViews en Interface Builder? ¿Debo crear una vista básica (UIView o NSView) y asignar su tipo a WKWebView? La mayoría de los ejemplos que encuentro en línea lo agregan a una vista de contenedor mediante programación; ¿es mejor por alguna razón?

Adam Fox
fuente
3
Esto sigue siendo un error abierto: lists.webkit.org/pipermail/webkit-unassigned/2014-September/…
David H
4
Estoy usando Xcode 7.2, no veo WKWebView en la biblioteca de objetos. ¿Todavía no está disponible?
user3731622
10
Todavía no está presente en Xcode 8.
Ryan Ballantyne
1
Xcode 9.0.1 ahora es compatible con WKWebView en IB. Todas las respuestas aquí están ahora obsoletas.
smileBot
1
@smileBot La nueva versión de Xcode tiene WKWebView pero requiere que apuntes a iOS 11+. La respuesta de crx_au funciona mejor -> stackoverflow.com/a/40118654/757503
mikemike396

Respuestas:

70

Tienes razón, no parece funcionar. Si miras en los encabezados, verás:

- (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;

lo que implica que no puede crear una instancia de una plumilla.

Tendrás que hacerlo a mano en viewDidLoad o loadView.

EricS
fuente
Ojalá cambien eso en algún momento, pero supongo que no es gran cosa. Johan me señaló el video de la WWDC, e incluso ellos están creando instancias en código.
Adam Fox
3
En el lado positivo, hacerlo en código le permitirá usar UIWebView en iOS 7 y WKWebView en iOS 8.
EricS
Bueno, después de esta respuesta, creo que puede anular este método en una subclase de WKWebView y luego usarlo en el generador de interfaces ... pero sin super.initWithCoder ... ¡probemos! :)
zarghol
1
Después de una prueba personal: no puede ... "No se puede anular 'init' que se ha marcado como no disponible" muy mal ...
zarghol
2
Una cosa razonable que se puede hacer es crear una subvista de UIView llamada MyWebView que tenga un WKWebView o un UIWebView como subvista, determinada en tiempo de ejecución en initWithCoder. Entonces puede crear vistas MyWebView en IB. Si está utilizando Swift, incluso puede agregar propiedades que se pueden editar en IB utilizando la palabra clave IBInspectable.
EricS
65

Como señalaron algunos, a partir de Xcode 6.4, WKWebView todavía no está disponible en Interface Builder. Sin embargo, es muy fácil agregarlos mediante código.

Solo estoy usando esto en mi ViewController. Omitir el constructor de interfaz

import UIKit
import WebKit

class ViewController: UIViewController {

    private var webView: WKWebView?

    override func loadView() {
        webView = WKWebView()

        //If you want to implement the delegate
        //webView?.navigationDelegate = self

        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        if let url = URL(string: "https://google.com") {
            let req = URLRequest(url: url)
            webView?.load(req)
        }
    }
}
Johan
fuente
13
Esta pregunta es específicamente sobre Interface Builder, por lo que esta respuesta no es muy útil.
Steve Landey
5
Tienes razón. Debería haber comenzado la respuesta con "No se puede crear WKWebView desde IB" :) Pero veo el video de la sesión "Introducción a la API de WebKit moderna" de WWDC y están usando Xcode, así que supongo que esto es solo algo que no es ' t disponible para nosotros en este momento.
Johan
1
Acabo de ver el video nuevamente, y están instanciando su WKWebView en código. Está alrededor de la marca de las 17:20 en el video.
Adam Fox
3
¿Cómo creará un bucle infinito? Esto es de la descripción de loadView en la documentación de iOS. "El controlador de vista llama a este método cuando se solicita su propiedad de vista, pero actualmente es nula".
Johan
2
Hacer referencia al self.view interiorloadView()es lo que conduce a la recursividad infinita. La configuración de la vista, por otro lado, no solo está permitida, sino también requerida (manualmente o mediante el encadenamiento asuper. De lo contrario, se seguirá llamando mientras la vista lo esténil).
Nicolas Miari
29

Info.plist

agregue su configuración de seguridad de transporte Info.plist

 <key>NSAppTransportSecurity</key>
 <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
 </dict>

Xcode 9.1+

Usando el constructor de interfaces

Puede encontrar el elemento WKWebView en la biblioteca de objetos.

ingrese la descripción de la imagen aquí

Agregar vista mediante programación con Swift 5

let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true

Agregar vista mediante programación con Swift 5 (muestra completa)

import UIKit
import WebKit

class ViewController: UIViewController {

    private weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        initWebView()
        webView.loadPage(address: "http://apple.com")
    }

    private func initWebView() {
        let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
        view.addSubview(webView)
        self.webView = webView
        webView.translatesAutoresizingMaskIntoConstraints = false
        webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        webView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
        webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        webView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
    }
}

extension WKWebView {
    func loadPage(address url: URL) { load(URLRequest(url: url)) }
    func loadPage(address urlString: String) {
        guard let url = URL(string: urlString) else { return }
        loadPage(address: url)
    }
}
Vasily Bodnarchuk
fuente
No es una buena sugerencia asignar una nueva vista en el inicializador para WebView. La respuesta de crx_au es superior
Ilias Karim
¡¡No encontré ninguna otra cosa que funcione !! Entonces ... como dijo ILI4S, tal vez no sea una buena sugerencia, no sé si lo es o no, ¡pero funciona correctamente! Gracias hombre, me salvaste el día !! :)
Jorge Cordero
20

Con Xcode 8 esto ahora es posible, pero el medio para lograrlo es un poco complicado por decir lo menos. Pero bueno, una solución funcional es una solución funcional, ¿verdad? Dejame explicar.

InitWithCoder de WKWebView: ya no está anotado como "NS_UNAVAILABLE". Ahora se ve como se muestra a continuación.

- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

Comience subclasificando WKWebView y anule initWithCoder. En lugar de llamar a super initWithCoder, deberá usar un método de inicio diferente, como initWithFrame: configuration :. Ejemplo rápido a continuación.

- (instancetype)initWithCoder:(NSCoder *)coder
{
    // An initial frame for initialization must be set, but it will be overridden 
    // below by the autolayout constraints set in interface builder. 
    CGRect frame = [[UIScreen mainScreen] bounds];
    WKWebViewConfiguration *myConfiguration = [WKWebViewConfiguration new];

    // Set any configuration parameters here, e.g.
    // myConfiguration.dataDetectorTypes = WKDataDetectorTypeAll; 

    self = [super initWithFrame:frame configuration:myConfiguration];

    // Apply constraints from interface builder.
    self.translatesAutoresizingMaskIntoConstraints = NO;

    return self;
}

En su Storyboard, use un UIView y asígnele una clase personalizada de su nueva subclase. El resto es negocio como de costumbre (establecer restricciones de diseño automático, vincular la vista a una salida en un controlador, etc.).

Finalmente, WKWebView escala el contenido de manera diferente a UIWebView. Es probable que muchas personas quieran seguir el simple consejo de Suppress WKWebView para escalar el contenido para renderizarlo con el mismo aumento que UIWebView para hacer que WKWebView siga más de cerca el comportamiento de UIWebView en este sentido.

crx_au
fuente
2
también debería leerse self = [super initWithFrame:myFrame configuration:myConfiguration];si usa restricciones, no olvide establecerself.translatesAutoresizingMaskIntoConstraints = NO;
Mojo66
En lugar de usar los límites de la pantalla principal como un marco de marcador de posición, también puede usar CGRectZero y guardarse algo de código.
Ilias Karim
Buena solucion. Funciona bien.
Rayfleck
¡Funciona perfectamente! Buena solución para no tener que apuntar a iOS 11 para obtener restricciones de WKWebView en el guión gráfico.
mikemike396
20

Aquí hay una versión simple de Swift 3 basada en la excelente respuesta de crx_au .

import WebKit

class WKWebView_IBWrapper: WKWebView {
    required convenience init?(coder: NSCoder) {
        let config = WKWebViewConfiguration()
        //config.suppressesIncrementalRendering = true //any custom config you want to add
        self.init(frame: .zero, configuration: config)
        self.translatesAutoresizingMaskIntoConstraints = false
    }
}

Cree una UIView en Interface Builder, asigne sus restricciones y asígnela WKWebView_IBWrappercomo una clase personalizada, así:

Utilidades -> Ficha Inspector de identidad [1]

Gama
fuente
Excelente respuesta
Amr Hossam
6

Si aún enfrenta este problema en versiones recientes de Xcode, es decir, v9.2 +, simplemente importe Webkit a su ViewController:

#import <WebKit/WebKit.h>
  1. Antes de la solución: ingrese la descripción de la imagen aquí

  2. Después de la corrección:

ingrese la descripción de la imagen aquí

MrDEV
fuente
4

Esto aparentemente ahora está arreglado en Xcode 9b4. Las notas de la versión dicen "WKWebView está disponible en la biblioteca de objetos de iOS".

No he examinado más a fondo para ver si requiere iOS 11 o si es compatible con versiones anteriores.

EricS
fuente
¡Puedo confirmar que esto funciona! ¡Hurra! También funciona en guiones gráficos de macOS (al menos para aplicaciones dirigidas a macOS 10.13+).
Clifton Labrum
1

Puede crear una instancia y configurar un WKWebView en IB desde Xcode 9, no es necesario hacerlo en código. ingrese la descripción de la imagen aquí

Sin embargo, tenga en cuenta que su objetivo de implementación debe ser superior a iOS 10 o obtendrá un error en tiempo de compilación.

ingrese la descripción de la imagen aquí

atineoSE
fuente
0

En XCode versión 9.0.1, WKWebView está disponible en Interface Builder.

ManuelMB
fuente
0

He vinculado WebKit, ¡ahora está funcionando!

ejemplo

amanecer zhou
fuente
0

No estoy seguro de si esto ayuda, pero resolví el problema al incluir el marco WebKit para el objetivo. No lo inserte, solo vincule la referencia. Todavía uso el objeto WebView en IB y lo dejo caer en el ViewController en el que lo estoy incrustando y nunca he tenido un problema ...

He trabajado en 4 proyectos WKWebView MacOS ahora y WebView ha funcionado correctamente en cada proyecto.

ranTrek
fuente
-14

Esto funcionó para mí en Xcode 7.2 ...

Primero agregue la vista web como una salida UIWebView en el guión gráfico / IB. Esto le dará una propiedad como esta:

@property (weak, nonatomic) IBOutlet UIWebView *webView;

Luego, simplemente edite su código para cambiarlo a WKWebView.

@property (weak, nonatomic) IBOutlet WKWebView *webView;

También debe cambiar la clase personalizada a WKWebView en el inspector de identidad.

Pat McG
fuente
Probé esto con un proyecto de OS X, que no funciona, por lo que debe ser un caso especial para iOS.
henrikstroem
2
Lo que haces es como escribir a máquina. NO puede cambiar el tipo de webView en absoluto.
DawnSong