0

I successfully subclassed a WKWwebView in Swift. But now I also want to subclass the UIScrollView within that WKWebView. E.g. I'd like to do something like this (pseudo-code):

class CustomWKWebview : WKWebView {
  override init(frame: CGRect, configuration: WKWebViewConfiguration) {
    super.init(frame: frame, configuration: configuration)

    self.scrollView = CustomScrollView // <-- this is what I want
  }
}

Although this seems to be impossible since self.scrollView is declared as read-only by Apple.

Is there another way to subclass the scrollView inside WKWebView anyway?

Tafel
  • 1,017
  • 10
  • 17
  • Why do you want to subclass a UIScrollView? – Demented07 Aug 09 '21 at 12:14
  • @Demented07 Good question! I have to overwrite the `touchesBegan` method. I did this in the WKWebView itself, but for that to work I had to do: `self.scrollView.isUserInteractionEnabled = false` This prevented the scrollView from working properly (since you cannot scroll anymore). Therefore I'd like to instead subclass the UIScrollView and listen to touch events there. – Tafel Aug 09 '21 at 12:19
  • Ok so why do you need a tapGestureRecignizer for this, what are you going to use it for? (I'm trying to see if there is a work around to your problem). – Demented07 Aug 09 '21 at 12:50
  • The use case is a little complex. But I'll do my best to explain: I have two views in Swift: A GMapView and a WKWebView (using the Capacitor.js project). I want to have the WKWebView on top of the GMapView. Then I make the WKWebView transparent so that the GMapView can be seen. This has the advantage that I can fully customize the GMapView using the HTML and CSS inside WKWebView. However, because the WKWebView is now on top, it catches all the touch events. That means that the GMapView will not receive any events. To solve this I want to use `touchesBegan` and delegate those events. – Tafel Aug 09 '21 at 12:56
  • Also you do not have to subclass the scrollView you could instead add it as a subView for example *let scrollView: UIScrollView!* Then in loadView or some other appropriate life cycle method create the sub view and add it to the view as a sub view *scrollView = UIScrollView(frame: view.frame)* Finally *view.addSubView(scrollView)* – Demented07 Aug 09 '21 at 12:59
  • I am not sure if that is possible. The scrollView seems to be sort of embedded into the WKWebView by Apple themselves: https://developer.apple.com/documentation/webkit/wkwebview/1614784-scrollview – Tafel Aug 09 '21 at 13:01
  • Yeh it is embedded into the webView therefore adding a scroll view on top doesn't do anything it just what you were mentioning before prevents the web view from scrolling. But picks up touch events I'll see if I can find a way to add a tapGestureRecignizer to the webView instead, it might be possible. – Demented07 Aug 09 '21 at 13:07
  • Honestly, I'm not sure if there is a solution for this as Apple discourage scroll views being embedded within other scroll view, which is what would happen if it was possible to add a scroll view on top of the web view to handle touch events. I also do not believe it is possible for web views themselves to handle touch events. But I could be wrong. I hope you find a solution that works for you sorry I could not be of more help. – Demented07 Aug 09 '21 at 13:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/235786/discussion-between-tafel-and-demented07). – Tafel Aug 09 '21 at 13:15

1 Answers1

2

The scrollView of WKWebView is a special internal WKScrollView class (which can be confirmed by the following Objective-C Runtime Method).

print(object_getClass(yourWebView.scrollView))

Because of this, any scrollView object you use to replace that of a WKWebView must also inherit from WKScrollView. This is not recommended since it is a private WebKit Framework class to begin with. Using private APIs will cause your App to be rejected from the App Store (speaking from experience).

Looks like you initially wanted to customize another view, by adding HTML/CSS on-top of it. The requirement to subclass the scrollView emerged because you wanted gestures to "pass-through" the view presenting web content.

I suggest the following workarounds.

  1. Try adding the WebView inside of your GMapsView by adding it as a subView of GMapsView - addSubview

  2. Assuming your "GMapsView" is actually "GMSMapView" from the Google Maps SDK for iOS, checkout their Map Styling guide to see if it fits your requirement - Google Maps for iOS SDK Style Reference

  • Thanks for your answer. So if I understand correctly, the "real" answer here is: it's impossible. Unfortunately I cannot add the WebView as a subview of GMSMapView. It must be done the other way around. Also the styling options are not what I want as well. I want to show fully fledged HTMLElements on top of the MapView. – Tafel Aug 10 '21 at 08:38
  • I thought of maybe adding a view on top of both the GMSMapView and the WebView. Whenever I detect a touchevent there, I dispatch them to both views. Do you think that would be a valid workaround? – Tafel Aug 10 '21 at 08:40
  • It might be, but depends on how touch gestures are handled within GMSMapView and WebView. Their implementation of touch handling is not documented anywhere. If your only requirement is to show HTML elements on top of a Map, then use the [GMaps JS SDK](https://developers.google.com/maps/documentation/javascript) and [Custom Overlays](https://developers.google.com/maps/documentation/javascript/customoverlays) to load a map inside of a WebView. Also see [Similar Question](https://stackoverflow.com/questions/2150688/google-maps-how-to-add-html-elements-to-specific-coordinates). – Thisura Dodangoda Aug 10 '21 at 10:33