0

I am using SwiftUI, so I have a wrapper around the Webview, like this:

import SwiftUI
import WebKit


struct WebView: UIViewRepresentable {
    var url: String
    let webView = WKWebView()
    
    func makeUIView(context: Context) -> WKWebView  {
        webView.evaluateJavaScript("navigator.userAgent") { (result, error) in
            print(result as! String)
        }
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let request = URLRequest(url: URL(string:url)!)
        uiView.load(request)
    }
}

How can I execute a function/method whenever the user makes scroll to the website?

kike
  • 658
  • 1
  • 8
  • 26
  • 1
    Does this answer your question? [iOS WKWebView detect when reaches bottom](https://stackoverflow.com/questions/52871774/ios-wkwebview-detect-when-reaches-bottom) There are answers that show how to set a scroll delegate. – jnpdx Jan 19 '22 at 21:29
  • How can I implement a delegate using SwiftUI? – kike Jan 19 '22 at 21:41
  • Use a Coordinator – jnpdx Jan 19 '22 at 21:42

2 Answers2

2

To get scroll position updates you need to create coordinator and override makeCoordinator() method and return instance of your coordinator. In makeUIView(_:) method just assign scrollview delegate to context.coordinator (context object in provided in arguments of makeUIView method)

Pass binding from view to coordinator and coordinator is responsible to update that binding. Here is code for that

struct WebView: UIViewRepresentable {
    
    var url: String
    @Binding var contentOffset: CGPoint
    
    init(url: String, contentOffset: Binding<CGPoint>) {
        self.url = url
        _contentOffset = contentOffset
    }
    
    let webView = WKWebView()
    
    func makeUIView(context: Context) -> WKWebView  {
        webView.scrollView.delegate = context.coordinator // assign delegation
        
        webView.evaluateJavaScript("navigator.userAgent") { (result, error) in
            print(result as! String)
        }
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let request = URLRequest(url: URL(string:url)!)
        uiView.load(request)
    }
    
    func makeCoordinator() -> Coordinator {
        .init(contentOffset: $contentOffset) // create coordinator for delegation
    }
    
    
    class Coordinator: NSObject, UIScrollViewDelegate {
        
        @Binding var contentOffset: CGPoint
        
        init(contentOffset: Binding<CGPoint>) {
            _contentOffset = contentOffset
        }
        
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            contentOffset = scrollView.contentOffset
        }
    }
}

SPatel
  • 4,768
  • 4
  • 32
  • 51
  • WOW! Outstanding! It works as a charm! Thanks a lot – kike Jan 19 '22 at 23:14
  • How do you then use this inside another view? When trying to use the WebView alongside other content, the WebView now expects me to pass in contentOffset. How does this get generated/used? `Missing argument for parameter 'contentOffset' in call` – Case Silva May 03 '23 at 13:39
1

WebView with UIScrollViewDelegate support.

import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {

var url: URL
var scrollViewDelegate: UIScrollViewDelegate?

func makeUIView(context: Context) -> WKWebView {
    let webView = WKWebView()
    webView.scrollView.delegate = scrollViewDelegate
    return webView
}

func updateUIView(_ webView: WKWebView, context: Context) {
    let request = URLRequest(url: url)
    webView.load(request)
}}

Observable ScrollViewDetector

import UIKit.UIScrollView

final class ScrollViewDetector: NSObject, ObservableObject, UIScrollViewDelegate {

@Published var isScrolledEnd = false

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let contentLoaded = scrollView.contentSize.height > 1000
    let endOfContentReached = scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.size.height < 100
   
    if contentLoaded &&  endOfContentReached {
        isScrolledEnd = true
    }
}}

Agreement view with sticky footer button.

import SwiftUI

struct AgreementView: View {

@StateObject var scrollViewDetector = ScrollViewDetector()

var body: some View {
    GeometryReader { geometry in
        ZStack {
            WebView(url: URL(string: "YOUR URL")!, scrollViewDelegate: scrollViewDetector)
            
            // Footer Navigation Link
            NavigationLink {
                EmptyView()
            } label: {
                // Your Label
                Button("Accept")
                    .padding()
                    .background(Color.white.opacity(!scrollViewDetector.isScrolledEnd ? 0.9 : 0.0))
            }
            .position(x: geometry.width(0.5), y: geometry.height(0.9))
            .opacity(!scrollViewDetector.isScrolledEnd ? 0.6 : 1.0)
            .disabled(!scrollViewDetector.isScrolledEnd ? true : false)
        }
    }
}}