WKWebView no Interface Builder

95

Parece que os modelos de objeto IB no XCode 6 beta ainda estão criando objetos de estilo antigo (UIWebView para iOS e WebView para OSX). Esperançosamente, a Apple irá atualizá-los para o WebKit moderno, mas até então, qual é a melhor maneira de criar WKWebViews no Interface Builder? Devo criar uma exibição básica (UIView ou NSView) e atribuir seu tipo a WKWebView? A maioria dos exemplos que encontro online o adiciona a uma visualização de contêiner de forma programática; isso é melhor por algum motivo?

Adam Fox
fonte
4
Estou usando o Xcode 7.2, não vejo WKWebView na biblioteca de objetos. Ainda não está disponível?
user3731622
10
Ainda não está presente no Xcode 8.
Ryan Ballantyne
1
O Xcode 9.0.1 agora oferece suporte a WKWebView em IB. Todas as respostas aqui estão obsoletas.
smileBot
1
@smileBot A nova versão do Xcode tem WKWebView, mas requer que você direcione o iOS 11+. a resposta crx_au funciona melhor -> stackoverflow.com/a/40118654/757503
mikemike396

Respostas:

70

Você está correto - não parece funcionar. Se você olhar nos cabeçalhos, verá:

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

o que implica que você não pode instanciar um de um bico.

Você terá que fazer isso manualmente em viewDidLoad ou loadView.

EricS
fonte
Espero que eles mudem isso em algum momento, mas acho que não é grande coisa. Johan me apontou de volta para o vídeo WWDC, e até mesmo eles estão instanciando em código.
Adam Fox de
3
No lado positivo, fazer isso em código permitirá que você use UIWebView no iOS 7 e WKWebView no iOS 8.
EricS
Bem, depois dessa resposta, acho que você pode substituir esse método em uma subclasse de WKWebView e, em seguida, usá-lo no construtor de interface .. mas sem super.initWithCoder .. vamos testar! :)
zarghol
1
Depois de um teste pessoal: você não pode ... "Não é possível substituir 'init' que foi marcado como indisponível" muito ruim ...
zarghol
2
Uma coisa razoável a fazer pode ser criar uma subvisualização UIView chamada MyWebView que tem um WKWebView ou um UIWebView como subvisualização, determinado no tempo de execução em initWithCoder. Então você pode criar visualizações MyWebView no IB. Se você estiver usando o Swift, você pode até adicionar propriedades que podem ser editadas no IB usando a palavra-chave IBInspectable.
EricS
65

Como apontado por alguns, a partir do Xcode 6.4, WKWebView ainda não está disponível no Interface Builder. No entanto, é muito fácil adicioná-los via código.

Estou apenas usando isso no meu ViewController. Skipping Interface builder

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)
        }
    }
}
João
fonte
13
Esta pergunta é especificamente sobre o Interface Builder, portanto, esta resposta não é muito útil.
Steve Landey
5
Você está certo. Eu deveria ter começado a resposta com “Você não pode criar WKWebView do IB" :) Mas eu assisto ao vídeo da sessão “Apresentando a API Modern WebKit" da WWDC e eles estão usando o Xcode, então acho que isso é apenas algo que não é t disponível para nós no momento.
Johan
1
Acabei de conferir o vídeo novamente e eles estão instanciando seu WKWebView em código. Está em torno da marca de 17:20 no vídeo.
Adam Fox de
3
Como isso criará um loop infinito? Isso é da descrição de loadView na documentação do iOS. "O controlador de visualização chama este método quando sua propriedade de visualização é solicitada, mas atualmente é nula."
Johan
2
Referenciar self.view internamenteloadView()é o que leva à recursão infinita. Definir a visualização, por outro lado, não é apenas permitido, mas também obrigatório (manualmente ou por encadeamentosuper. Caso contrário, continuará sendo chamado enquanto a visualização fornil).
Nicolas Miari
29

Info.plist

adicione em sua configuração de segurança de transporte Info.plist

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

Xcode 9.1+

Usando o construtor de interface

Você pode encontrar o elemento WKWebView na biblioteca de objetos.

insira a descrição da imagem aqui

Adicionar visualização programaticamente com 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

Adicionar visualização programaticamente com Swift 5 (amostra 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
fonte
Não é uma boa sugestão alocar uma nova visão no inicializador para WebView. A resposta de crx_au é superior
Ilias Karim
Não encontrei nenhuma outra coisa que funcione !! Então ... como o ILI4S falou talvez não seja uma boa sugestão, não sei se é ou não, mas funciona bem! Obrigado cara, você salvou meu dia !! :)
Jorge Cordero
20

Com o Xcode 8 isso agora é possível, mas o meio de alcançá-lo é um pouco hacky, para dizer o mínimo. Mas hey, uma solução funcional é uma solução funcional, certo? Deixe-me explicar.

InitWithCoder de WKWebView: não está mais anotado como "NS_UNAVAILABLE". Agora se parece com o mostrado abaixo.

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

Comece criando uma subclasse de WKWebView e substitua initWithCoder. Em vez de chamar super initWithCoder, você precisará usar um método init diferente, como initWithFrame: configuration :. Exemplo rápido abaixo.

- (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;
}

Em seu Storyboard, use um UIView e dê a ele uma classe personalizada de sua nova subclasse. O resto é business as usual (definir restrições de layout automático, vincular a visualização a uma saída em um controlador, etc.).

Finalmente, WKWebView dimensiona o conteúdo de maneira diferente para UIWebView. Muitas pessoas provavelmente vão querer seguir o conselho simples em Suprimir WKWebView do escalonamento do conteúdo para renderizar na mesma ampliação que o UIWebView faz para fazer com que o WKWebView siga mais de perto o comportamento do UIWebView a esse respeito.

crx_au
fonte
2
deve ler self = [super initWithFrame:myFrame configuration:myConfiguration];também se você usar restrições, não se esqueça de definirself.translatesAutoresizingMaskIntoConstraints = NO;
Mojo66
Em vez de usar os limites da tela principal como um espaço reservado, você também pode usar o CGRectZero e salvar algum código.
Ilias Karim
Ótima solução. Funciona bem.
Rayfleck de
Funciona perfeitamente! Boa correção para não ter que direcionar o iOS 11 para obter restrições WKWebView no storyboard.
mikemike396
20

Aqui está uma versão simples do Swift 3 baseada na excelente resposta 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
    }
}

Crie um UIView no Interface Builder, atribua suas restrições e atribua-o WKWebView_IBWrappercomo uma classe personalizada, assim:

Utilitários -> Guia Inspetor de identidade [1]

Gama
fonte
Excelente resposta
Amr Hossam
6

Se você ainda enfrenta esse problema nas versões recentes do Xcode, ou seja, v9.2 +, basta importar o Webkit para o seu ViewController:

#import <WebKit/WebKit.h>
  1. Antes da correção: insira a descrição da imagem aqui

  2. Após a correção:

insira a descrição da imagem aqui

MrDEV
fonte
4

Isso agora está aparentemente corrigido no Xcode 9b4. As notas de lançamento dizem "WKWebView está disponível na biblioteca de objetos iOS."

Não olhei mais fundo para ver se ele requer iOS 11 ou é compatível com versões anteriores.

EricS
fonte
Posso confirmar que isso funciona! Hooray! Ele também funciona em storyboards do macOS (pelo menos para aplicativos voltados para o macOS 10.13+).
Clifton Labrum
1

Você pode instanciar e configurar um WKWebView no IB desde o Xcode 9, sem necessidade de fazer isso no código. insira a descrição da imagem aqui

Observe que seu destino de implantação deve ser superior ao iOS 10 ou você obterá um erro de tempo de compilação.

insira a descrição da imagem aqui

atineoSE
fonte
0

No XCode Versão 9.0.1, WKWebView está disponível no Interface Builder.

ManuelMB
fonte
0

Vinculei o WebKit, agora está funcionando!

exemplo

amanhecer zhou
fonte
0

Não tenho certeza se isso ajuda, mas resolvi o problema para mim incluindo a estrutura do WebKit para o destino. Não incorpore, apenas vincule a referência. Eu ainda uso o objeto WebView no IB e coloco-o no ViewController no qual estou incorporando e nunca tive um problema ...

Eu trabalhei em 4 projetos WKWebView MacOS agora e o WebView funcionou corretamente em cada projeto.

ranTrek
fonte
-14

Isso funcionou para mim no Xcode 7.2 ...

Primeiro adicione a visualização da web como uma saída UIWebView no storyboard / IB. Isso lhe dará uma propriedade como esta:

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

Em seguida, edite o código para alterá-lo para WKWebView.

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

Você também deve alterar a classe personalizada para WKWebView no inspetor de identidade.

Pat McG
fonte
Tentei fazer isso com um projeto OS X - que não funciona, então deve ser um caso especial para iOS.
Henrikstroem
2
O que você faz é como um tipo de elenco. Ele NÃO pode alterar o tipo do webView.
DawnSong