WKWebView scrolls to the top after keyboard opens - ios

I have a WKWebView app, and in the url it loads it's got editables (inputs) to type in. But in the ios app when you tap an input that's at the bottom of the page, it opens the keyboard as expected, scrolls down on the page so you can see the input as expected, then it scrolls back to the top after half a second. So I tried it in the browser, but it didn't scroll back up after a split second. Does anyone know why this is happening only in my ios app?
Here's my code:
ContentView.swift:
struct ContentView: View {
var body: some View {
WebView(url: URL(string: "https://www.mywebsitenamethatiwontshare.com")!)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
WebView.swift:
struct WebView: UIViewRepresentable {
var url: URL
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ webView: WKWebView, context: Context) {
let request = URLRequest(url: url)
webView.load(request)
}
}

It is possible you might be causing a refresh of the view in your updateUIView function. Try removing the webView.load inside and try
struct WebView: UIViewRepresentable {
var url: URL
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
let request = URLRequest(url: url)
webView.load(request)
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
// DO NOTHING
}
}

Related

Constraints Warnings when TextField becomes Active in WKWebView

I am using a WKWebView to display a webpage in my iOS app. However, when a text field within the webpage becomes active, I am receiving constraints warnings in the console.
Implementation:
struct WKWebViewRepresentable: UIViewRepresentable {
typealias UIViewType = WKWebView
var url: URL
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.navigationDelegate = context.coordinator
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
let request = URLRequest(url: url)
webView.load(request)
}
func makeCoordinator() -> WKWebViewRepresentableCoordinator {
WKWebViewRepresentableCoordinator()
}
}
Use:
.fullScreenCover(item: $viewModel.activeSheet) { activeSheet in
switch activeSheet {
case .addPaymentMethodLocation(let url):
WKWebViewRepresentable(url: url)
}
}
Output:
debug logs

How to open external links in a WebView Swift?

I've a question about opening external links in my webView of SwiftUI. The web page loaded properly, but I can't open external links from this website (for example: Facebook, YouTube, Mail, Phone etc). Please, who can give me a help on this one?
I added the code below:
You can call an url, like ShowWebpage("https://www.wordpress.com"), in the ContentView.
struct WebView: UIViewRepresentable {
let request: URLRequest
private var webView: WKWebView?
init(request: URLRequest) {
self.webView = WKWebView()
self.request = request
self.webView?.configuration.defaultWebpagePreferences.allowsContentJavaScript = true
}
func makeUIView(context: Context) -> WKWebView {
return webView!
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.load(request)
}
}
struct ShowWebpage : View {
//get URL Page
var urlpage: String
var body: some View {
let webview = WebView(request: URLRequest(url: URL(string: urlpage)!))
VStack{
//Load URL page
webview
}
}
}

SwiftUI: WebView doesn't load specific page

I try to load a url in my WebView, it works well with "https://www.google.com", but it doesn't work with "https://www.sl.se".
It shows "Not Found, HTTP Error 404: The requested resource is not found."
The weird thing is that, if I google "sl.se" in Google page, click the sl.se, the the "www.sl.se" can be load the WebView. Does anyone know the reason?
ContentView.swift
// ContentView.swift
struct ContentView: View {
#State private var shouldRefresh = false
var body: some View {
VStack{
Button(action: {
self.shouldRefresh.toggle()
}){
Text("Reload")
}
WebView(url: nil, reload: $shouldRefresh)
}
}
}
WebView.Swift
// WebView.swift
import WebKit
import SwiftUI
struct WebView: UIViewRepresentable{
var url: URL? // optional, if absent, one of below search servers used
#Binding var reload: Bool
private let urls = [URL(string: "https://google.com/")!, URL(string: "https://www.sl.se")!]
private let webview = WKWebView()
fileprivate func loadRequest(in webView: WKWebView) {
if let url = url {
webView.load(URLRequest(url: url))
} else {
let index = Int(Date().timeIntervalSince1970) % 2
print("load: \(urls[index].absoluteString)")
webView.load(URLRequest(url: urls[index]))
}
}
func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
loadRequest(in: webview)
return webview
}
func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<WebView>) {
if reload {
loadRequest(in: uiView)
DispatchQueue.main.async {
self.reload = false // must be async
}
}
}
}
There is nothing wrong with your code. You just need to remove the www from your URL. try https://sl.se and It'll work.

WKWebview Prevent updateUIView

When creating the WKWebView I pass the web page URL via #Binding webPageURL.
I also update the displayed website via webPageURL.
The problem here is that when I call a NavigationLink and navigate back from the NavigationLink, the updateUIView method is called and the WKWebView reloads the web page.
How do I prevent the WKWebView from calling the method updateUIView when I navigate back from the NavigationLink?
struct MainView:View {
#State private var isActive = false
#Binding var stateWebPageURL:String
var body: some View {
return VStack {
MyWKWebView(webPageURL:$stateWebPageURL)
NavigationLink(destination: SecoundDestination(),isActive: $isActive) {
Text("Do Something")
}
}
}
}
struct MyWKWebView: UIViewRepresentable {
#Binding var webPageURL:String
func updateUIView(_ uiView: WKWebView, context: Context) {
let pageURL = URL(string:webPageURL)
let urlRequest = URLRequest(url: pageURL!)
uiView.load(urlRequest)
}
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView(frame: .zero,configuration: webConfiguration)
let pageURL = URL(string:webPageURL)
let urlRequest = URLRequest(url: pageURL!)
webView.load(urlRequest)
return webView
}
func makeCoordinator() -> ContentController {
ContentController()
}
}
Provided code is not testable, so just by code reading, the idea is to make explicitly MyWKWebView equatable, as below
var body: some View {
return VStack {
MyWKWebView(webPageURL:$stateWebPageURL).equatable() // << here !!
and
struct MyWKWebView: UIViewRepresentable, Equatable {
static func == (lhs: MyWKWebView, rhs: MyWKWebView) -> Bool {
lhs.webPageURL == rhs.webPageURL
}
// ... other your code
SwiftUI should not update equal views, so should work.

Load an Activity Indicator while website is loading in SwiftUI

I have a basic list that displays a webview. I want to add an activity indicator that shows while the webpage is loading. this is the code that I've created.
import SwiftUI
import WebKit
struct WebView: UIViewRepresentable {
var url: String
// makeUIView func
func makeUIView(context: Context) -> WKWebView {
guard let url = URL(string: self.url) else {
return WKWebView()
}
let request = URLRequest(url: url)
let wkWebView = WKWebView()
wkWebView.load(request)
return wkWebView
}
// updateUIView func
func updateUIView(_ uiView: WKWebView, context: Context) {
}
}
struct WebView_Preview: PreviewProvider {
static var previews: some View {
WebView(url: "https://www.google.com")
}
}
Thank you!
Here's something that should do what you want. The WebView has a delegate in the Coordinator class. It changes a binding, to which the ContentView can react appropriately. Currently it's just a Text displaying the raw value of the state, but it can be replaced with an activity indicator of some sorts.
struct ContentView: View {
#State var urlString = ""
#State var workState = WebView.WorkState.initial
var body: some View {
VStack(spacing: 20) {
WebView(urlString: self.$urlString, workState: self.$workState)
Button("Play") {
self.urlString = "https://www.example.com/"
}
Text("Current work = " + self.workState.rawValue)
}
}
}
struct WebView: UIViewRepresentable {
enum WorkState: String {
case initial
case done
case working
case errorOccurred
}
#Binding var urlString: String
#Binding var workState: WorkState
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.navigationDelegate = context.coordinator
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
switch self.workState {
case .initial:
if let url = URL(string: self.urlString) {
uiView.load(URLRequest(url: url))
}
default:
break
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebView
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
self.parent.workState = .working
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.parent.workState = .errorOccurred
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.parent.workState = .done
}
init(_ parent: WebView) {
self.parent = parent
}
}
}
You will need to add an Coordinator to your UIViewRepresentable.
I think the answers for this question give you the right ideas.
SwiftUI WKWebView detect url changing

Resources