I have a swipe gesture recognizer to bring a toolbar off and on the screen. It was working beautifully until I added a JavaScript to iOS bridge using WKUserContentController(). The bridge works, successfully printing the JavaScript message, but swiping no longer works. But if I comment out Webview = WKWebView(frame: self.view.frame, configuration: configuration) and view.addSubview(Webview) the bridge is obviously broken, but the gesture recognizer works. I just can't get them both working at the same time. Where have I gone wrong?
Edit: Maybe this is relevant: with the code as below, the activity indicator does not appear when the WebView is loading. Also, if I comment out the lines that start the toolbar hidden (starting point swiped off the page), the toolbar should appear, but it doesn't:
import UIKit
import WebKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate,
WKNavigationDelegate, WKScriptMessageHandler {
let locationManager = CLLocationManager()
#IBOutlet var Webview: WKWebView!
#IBOutlet var ActInd: UIActivityIndicatorView!
#IBOutlet var Toolbar: UIToolbar!
override func viewDidLoad() {
super.viewDidLoad()
let controller = WKUserContentController()
controller.add(self, name: "SwiftNative")
let configuration = WKWebViewConfiguration()
configuration.userContentController = controller
// commenting out these two lines disables the bridge but activates swiping again
Webview = WKWebView(frame: self.view.frame, configuration: configuration)
view.addSubview(Webview)
let url = URL(string: "https://www.bazleysawesomesite.com")
let request = URLRequest(url: url!)
Webview.load(request)
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(_:)))
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(_:)))
rightSwipe.direction = .right
leftSwipe.direction = .left
view.addGestureRecognizer(rightSwipe)
view.addGestureRecognizer(leftSwipe)
self.view.backgroundColor = UIColor.black
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("Message from beyond: \(message.body)")
}
// Make the status bar text light
override var preferredStatusBarStyle : UIStatusBarStyle {
return UIStatusBarStyle.lightContent
}
// Start the toolbar "hidden"
// commenting out these lines should make the toolbar appear, but it doesn't
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
Toolbar.center.x = Toolbar.center.x - self.view.frame.width
}
// Activity Indicator stuff
func webViewDidStartLoad(_ : WKWebView) {
ActInd.startAnimating()
}
func webViewDidFinishLoad(_ : WKWebView) {
ActInd.stopAnimating()
if self.Toolbar.center.x > 0 {
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.Toolbar.center.x = self.Toolbar.center.x - self.view.frame.width
})
}
}
// handle swipes
func handleSwipes(_ sender:UISwipeGestureRecognizer) {
if (sender.direction == .right) {
if self.Toolbar.center.x < 0 {
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.Toolbar.center.x = self.Toolbar.center.x + self.view.frame.width
})
}
}
if (sender.direction == .left) {
if self.Toolbar.center.x > 0 {
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.Toolbar.center.x = self.Toolbar.center.x - self.view.frame.width
})
}
}
}
}
Related
I have a class called ThemeVC which has a textview (connected with an IBoutlet) and functionalities applied to it (it has a recognizer that detects the tapped words).
My goal here is that I would like to extract that piece of functionality, and put it maybe in its own class or create a delegate so I could reuse that functionality on other textviews.
Anyone knows how?
I pasted my code below.
(HERE comments, are functions that should be called from any view controller)
import UIKit
class ThemeVC: UIViewController, UITextViewDelegate, UINavigationControllerDelegate {
#IBOutlet weak var themeTextView: UITextView!
var tB = UIBarButtonItem()
// Move away from ThemeVC ... ->
var selectionDict = [String:Int]()
var viewTagCount = Int()
var tap = UIGestureRecognizer()
var firstTimeGrouped = false
// -> ... Move away from ThemeVC
override func viewDidLoad() {
super.viewDidLoad()
themeTextView.delegate = self
loadbuttons ()
//HERE
addTagSelectorToolBar ()
}
func loadbuttons () {
tB = UIBarButtonItem(image: UIImage(systemName: "hand.point.up.left"), style: .plain, target: self, action: #selector(getTag(sender:)))
navigationItem.rightBarButtonItems = [tB]
}
#objc func getTag(sender: AnyObject) {
themeTextView.resignFirstResponder()
//HERE
startTagSelection()
}
}
// Move away from ThemeVC ... ->
extension ThemeVC {
func startTagSelection () {
navigationController?.setToolbarHidden(false, animated: false)
tap.isEnabled = true
tB.isEnabled = false
themeTextView.isEditable = false
themeTextView.isSelectable = false
}
}
extension ThemeVC {
#objc func doneTagSelection(){
navigationController?.setToolbarHidden(true, animated: false)
tap.isEnabled = false
tB.isEnabled = true
themeTextView.isEditable = true
themeTextView.isSelectable = true
firstTimeGrouped = false
}
}
extension ThemeVC {
func addTagSelectorToolBar (){
addTappedTagRecognizer()
tap.isEnabled = false
let done = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTagSelection))
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
toolbarItems = [spacer, done]
}
}
extension ThemeVC {
func addTappedTagRecognizer () {
tap = UITapGestureRecognizer(target: self, action: #selector(tapResponse(recognizer:)))
tap.delegate = self as? UIGestureRecognizerDelegate
themeTextView.addGestureRecognizer(tap)
}
#objc private func tapResponse(recognizer: UITapGestureRecognizer) {
let location: CGPoint = recognizer.location(in: themeTextView)
let position: CGPoint = CGPoint(x:location.x, y:location.y)
let tapPosition: UITextPosition? = themeTextView.closestPosition(to:position)
if tapPosition != nil {
let textRange: UITextRange? = themeTextView.tokenizer.rangeEnclosingPosition(tapPosition!, with: UITextGranularity.word, inDirection: UITextDirection(rawValue: 1))
if textRange != nil
{
let tappedWord: String? = themeTextView.text(in:textRange!)
print(tappedWord ?? "Unable to get word")
}
}
}
}
// ... -> Move away from ThemeVC
How to test my code:
Create a new project with a storyboard
On the left hand side rename viewcontroller with themeVC, and replace
its code with the code I gave.
On the storyboard, embed the controller in a navigation controller, on right side, change in identity inspector class from view controller to themeVC
add a textview and link it to the IBoutlet
Looking at the parts you want to move away from ThemeVC, I would have to say not everything should be moved away from ThemeVC.
For example, you marked startTagSelection as something you want to move away, but you reference the navigationController which belongs to the view controller so it should ideally not be the responsibility of your UITextView to update your UINavigationBar.
So the two ideas discussed in the comments was using SubClasses and Protocols.
Protocols was the suggestion of Ptit Xav so I will show one way that could be used, Ptit Xav could add an answer if something else was in mind.
I start with creating a protocol
// Name the protocol as you see appropriate
// I add #objc so it can be accessible from Storyboard
// This will be used to `hand over` responsibility of
// a certain action / event
#objc
protocol CustomTextViewTagDelegate: class {
func customTextViewDidStartSelection(_ textView: CustomTextView)
func customTextViewDidFinishSelection(_ textView: CustomTextView)
}
Next I subclass a UITextView to add my own customization
#IBDesignable
class CustomTextView: UITextView {
var selectionDict = [String:Int]()
var viewTagCount = Int()
var tap = UIGestureRecognizer()
var firstTimeGrouped = false
// Name it as you wish
// #IBInspectable added for storyboard accessibility
// You could also make it an IBOutlet if your prefer
// that interaction
#IBInspectable
weak var tagDelegate: CustomTextViewTagDelegate?
func startTagSelection () {
// Remove the commented lines as this should the responsibility of
// the view controller, manage in the view controller using the delegate
// navigationController?.setToolbarHidden(false, animated: false)
// tB.isEnabled = false
tap.isEnabled = true
isEditable = false
isSelectable = false
// Hand over responsibility of this action back whatever
// has subscribed as the delegate to implement anything else
// for this action
tagDelegate?.customTextViewDidStartSelection(self)
}
func addTappedTagRecognizer () {
tap = UITapGestureRecognizer(target: self,
action: #selector(tapResponse(recognizer:)))
tap.delegate = self as? UIGestureRecognizerDelegate
addGestureRecognizer(tap)
}
#objc private func tapResponse(recognizer: UITapGestureRecognizer) {
let location: CGPoint = recognizer.location(in: self)
let position: CGPoint = CGPoint(x:location.x,
y: location.y)
let tapPosition: UITextPosition? = closestPosition(to:position)
if tapPosition != nil {
let textRange: UITextRange? = tokenizer.rangeEnclosingPosition(tapPosition!,
with: UITextGranularity.word,
inDirection: UITextDirection(rawValue: 1))
if textRange != nil
{
let tappedWord: String? = text(in:textRange!)
print(tappedWord ?? "Unable to get word")
}
}
}
#objc func doneTagSelection() {
// This is not the text view's responsibility, manage in the
// view controller using the delegate
// navigationController?.setToolbarHidden(true, animated: false)
// tB.isEnabled = true
tap.isEnabled = false
isEditable = true
isSelectable = true
firstTimeGrouped = false
// Hand over responsibility of this action back whatever
// has subscribed as the delegate to implement anything else
// for this action
tagDelegate?.customTextViewDidFinishSelection(self)
}
}
And finally use it like so
class ThemeVC: UIViewController {
// Change UITextView to CustomTextView
#IBOutlet weak var themeTextView: CustomTextView!
var tB = UIBarButtonItem()
// If you do not set up the delegate in your
// storyboard, you need to it in your code
// call this function from didLoad or something
// if needed
private func configureTextView() {
themeTextView.tagDelegate = self
}
// All your other implementation
}
extension ThemeVC: CustomTextViewTagDelegate {
func customTextViewDidStartSelection(_ textView: CustomTextView) {
navigationController?.setToolbarHidden(false,
animated: false)
tB.isEnabled = false
}
func customTextViewDidFinishSelection(_ textView: CustomTextView) {
navigationController?.setToolbarHidden(true,
animated: false)
tB.isEnabled = true
}
}
I did not add addTagSelectorToolBar as part of the CustomTextView implementation as this is not a good candidate to be part of that module as all of its code is related to the view controller so i don't recommend making a part of the CustomTextView implementation.
Is it possible to detect a back swipe in a web view when there has not been any forward navigation? The following code works fine if the user has moved off the first page but I want to know if they back swiped while on that page.
Update - This is the code I'm using after modifying it per comments. I added the navigation back function and the swipe gesture.
class WebViewClient: UIViewController, WKUIDelegate, WKNavigationDelegate, UIGestureRecognizerDelegate
{
var webView: WKWebView!
public func load(pageLink page: String)
{
print("Opening: " + page)
if webView != nil
{
if let myURL = URL(string:page)
{
webUrlRequest = page
let myRequest = URLRequest(url: myURL)
webView.load(myRequest)
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
{
print("Opened: " + webUrlRequest)
}
override func loadView()
{
let pref = WKPreferences()
pref.javaScriptEnabled = true
let config = WKWebViewConfiguration()
config.preferences = pref
webView = WKWebView(frame: .zero, configuration: config)
webView.uiDelegate = self
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = true
view = webView
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void)
{
if navigationAction.navigationType == .backForward && webView.backForwardList.backList.count == 0
{
print("Can't go back any more")
}
decisionHandler(.allow)
}
#objc func backNavigationFunction(_ sender: UIScreenEdgePanGestureRecognizer)
{
let dX = sender.translation(in: view).x
if sender.state == .ended
{
let fraction = abs(dX / view.bounds.width)
if fraction >= 0.35
{
print("Back swipe")
//back navigation code here
}
}
}
override func viewDidLoad()
{
super.viewDidLoad()
let swipeGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(backNavigationFunction(_:)))
swipeGesture.edges = .left
swipeGesture.delegate = self
webView.addGestureRecognizer(swipeGesture)
}
}
I would implement a UIScreenEdgePanGestureRecognizer to handle this. Add the following to your viewDidLoad
let swipeGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(backNavigationFunction(_:)))
swipeGesture.edges = .left
swipeGesture.delegate = self
view.addGestureRecognizer(swipeGesture)
Then, implement a function to handle the backNavigationFunction like below:
#objc func backNavigationFunction(_ sender: UIScreenEdgePanGestureRecognizer) {
let dX = sender.translation(in: view).x
if sender.state == .ended {
let fraction = abs(dX / view.bounds.width)
if fraction >= 0.35 {
//back navigation code here
}
}
}
Don't forget to make the ViewController inherit from the UIGestureRecognizerDelegate class (i.e. class WebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, UIGestureRecognizerDelegate). The delegate in question here is UIGestureRecognizerDelegate but I anticipate you'll need the others as well.
in this I had implemented the swipe gestures on image view and it is embedded in scroll view but gestures are not working here is my code any solution for this ?
collectionView.delegate = self
collectionView.dataSource = self
imageView.isUserInteractionEnabled = true
let swipeLeft = UISwipeGestureRecognizer(target: self, action:#selector(swiped(gesture:)))
swipeLeft.direction = .left
self.imageView.addGestureRecognizer(swipeLeft)
swipeLeft.cancelsTouchesInView = false
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(swiped(gesture:)))
swipeRight.direction = .right
self.imageView.addGestureRecognizer(swipeRight)
swipeRight.cancelsTouchesInView = false
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(imageTapped(_:)))
self.imageView.addGestureRecognizer(tap)
If you want swipe and scrolling both at work concurrently you have to implement gesture recognizer delegate.
// here are those protocol methods with Swift syntax
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
return true
}
May be you could try this.Don't forget to delegate as self. :)
For your case, you want to have UI like Gallery View. Just Left and right swipe to change photo and tap/DoubleTap to zoom.
Don't need Swipe Gestures.
Just use Collection view with paging enabled. Each cell will display a single image. when Paging enabled, only a single cell will be shown at a time.
So just enable Paging enabled in collection view and try running.
If you want to zoom, when tapping a cell then, add TapGesture to CollectionView's parent UIScrollView and do write actions correspondingly.
For this case, I've used custom collection view cell.
I've just added my custom cell code for your reference as given below.
class ImageViewerCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func awakeFromNib() {
super.awakeFromNib()
self.scrollView.minimumZoomScale = 1.0
self.scrollView.maximumZoomScale = 6.0
self.scrollView.delegate = self
self.scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
self.scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: scrollView.frame.size), animated: true)
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doubleTapAction(_:)))
doubleTap.numberOfTapsRequired = 2
self.scrollView.addGestureRecognizer(doubleTap)
}
func setURL(imageURL : URL, needLoader : Bool) {
DispatchQueue.main.async {
self.scrollView.setZoomScale(self.scrollView.minimumZoomScale, animated: true)
self.scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: self.scrollView.frame.size), animated: true)
if needLoader {
self.activityIndicator.isHidden = false
self.activityIndicator.startAnimating()
self.imageView.pin_setImage(from: imageURL, completion: { (completed) in
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
})
}
else {
self.activityIndicator.isHidden = true
self.imageView.pin_setImage(from: imageURL, placeholderImage: placeHolderImage)
}
self.imageView.pin_updateWithProgress = true
}
}
#IBAction func doubleTapAction(_ sender: Any) {
if scrollView.zoomScale == scrollView.minimumZoomScale {
let touchPoint = (sender as! UITapGestureRecognizer).location(in: scrollView)
let scale = min(scrollView.zoomScale * 3, scrollView.maximumZoomScale)
let scrollSize = scrollView.frame.size
let size = CGSize(width: scrollSize.width / scale,
height: scrollSize.height / scale)
let origin = CGPoint(x: touchPoint.x - size.width / 2,
y: touchPoint.y - size.height / 2)
scrollView.zoom(to:CGRect(origin: origin, size: size), animated: true)
}
else {
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
scrollView.zoom(to: CGRect(origin: CGPoint.zero, size: scrollView.frame.size), animated: true)
}
}
}
extension ImageViewerCollectionViewCell : UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.imageView
}
}
As given above, I've used a imageview inside UIScrollView. So when you double tapped that scroll view, scrollview will zoom in & out.
Hope you understand and hope it helps for sure.
I'm working on a webView based screen and I want to have an activity indicator to spin while the web content is loading.
Since I have to use the same activity indicator in another screen of the app, I've put the code in static functions in a specific file.
The activity indicator seems to work fine for some web (simple) web pages but I have an issue when I load more complex pages. The activity indicators gets duplicate several times. (See screenshot below)
On the screenshot, the first activity indicator has the correct layout but the one below is darker which implies that several other activity indicators have been overlaid on top of each other. And then they never disappears.
When it comes to code:
I have a webView and two delegate methods controlling the activity indicators.
func webViewDidStartLoad(_ webView: UIWebView) {
DispatchQueue.main.async {
EBUtil.startActivityIndicator(self)
}
}
func webViewDidFinishLoad(_ webView: UIWebView) {
DispatchQueue.main.async {
EBUtil.stopActivityIndicator(self)
}
}
I guess it comes from the fact that webViewDidStartLoad gets called several times. Any idea how I can prevent this behaviour to happen?
Thanks in advance.
Edouard
EDIT:
Here is the full code for my VC.
class NewsDetailsViewController: UIViewController, UIWebViewDelegate {
//MARK: - #IBOUTLETS
#IBOutlet weak var webView: UIWebView!
//MARK: - VARIABLES
var url: String!
//MARK: - APP LIFE CYCLE
override func viewDidLoad() {
super.viewDidLoad()
if url != nil {
let urlToLoad = NSURL(string: url)
webView.loadRequest(NSURLRequest(url: urlToLoad as! URL) as URLRequest)
}
}
func webViewDidStartLoad(_ webView: UIWebView) {
DispatchQueue.main.async {
EBUtil.startActivityIndicator(self)
}
}
func webViewDidFinishLoad(_ webView: UIWebView) {
DispatchQueue.main.async {
EBUtil.stopActivityIndicator(self)
}
}
}
And code for activity indicator start and stop:
static func startActivityIndicator(_ sender: UIViewController) {
print("START ANIMATING")
if let view = sender.view {
activityIndicatorBackground = UIView(frame: CGRect(x: view.center.x - 25, y: view.center.y - 25, width: 50, height: 50))
activityIndicatorBackground.backgroundColor = UIColor.black
activityIndicatorBackground.layer.zPosition = CGFloat(MAXFLOAT-1)
activityIndicatorBackground.alpha = 0.6
activityIndicatorBackground.layer.cornerRadius = 5
view.addSubview(activityIndicatorBackground)
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
activityIndicator.center = view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.white
activityIndicator.layer.zPosition = CGFloat(MAXFLOAT)
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
}
}
static func stopActivityIndicator(_ sender: UIViewController) {
print("STOP ANIMATING")
activityIndicatorBackground.removeFromSuperview()
activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
}
try this -
add below line in your viewController class -
var activityIndicator: UIActivityIndicatorView?
And update your startActivityIndicator stopActivityIndicator method like below-
func startActivityIndicator(_ sender: UIViewController) {
print("START ANIMATING")
if let view = sender.view {
if activityIndicator != nil {
activityIndicator?.removeFromSuperview()
}
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
activityIndicator?.center = view.center
activityIndicator?.layer.cornerRadius = 10
activityIndicator?.backgroundColor = UIColor.gray
activityIndicator?.hidesWhenStopped = true
activityIndicator?.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.white
activityIndicator?.layer.zPosition = CGFloat(MAXFLOAT)
view.addSubview(activityIndicator!)
activityIndicator?.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
}
}
func stopActivityIndicator(_ sender: UIViewController) {
print("STOP ANIMATING")
activityIndicator?.stopAnimating()
activityIndicator?.removeFromSuperview()
UIApplication.shared.endIgnoringInteractionEvents()
}
Hope it will work for you :)
I've been having trouble getting a WKWebView in Swift to trigger some javascript in the loaded web page. Im just trying to use javascript to change a background color on the website if the user swipes left, swipes right, or clicks a button. Any help is greatly appreciated.
import UIKit
import WebKit
class myViewController: UIViewController, WKNavigationDelegate {
#IBOutlet var container: UIView!
var webView: WKWebView?
override func viewDidLoad() {
super.viewDidLoad()
self.webView = WKWebView()
container.addSubview(webView!)
webView?.allowsBackForwardNavigationGestures = true
webView?.navigationDelegate = self
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(myViewController.handleSwipes(_:)))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(myViewController.handleSwipes(_:)))
leftSwipe.direction = .Left
rightSwipe.direction = .Right
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
}
override func viewDidAppear(animated: Bool) {
let frame = CGRectMake(0, 0, container.bounds.width, container.bounds.height)
webView!.frame = frame
let url = NSURL(string: "https://www.google.com")
let request = NSURLRequest(URL: url!)
self.webView!.loadRequest(request)
}
#IBAction func buttonClick(sender: AnyObject) {
webView?.evaluateJavaScript("document.getElementByID('hplogo').style.backgroundColor='red';", completionHandler: nil)
}
func handleSwipes(sender:UISwipeGestureRecognizer){
if (sender.direction == .Left){
webView?.evaluateJavaScript("document.getElementByID('hplogo').style.backgroundColor='black';", completionHandler: nil)
}
if (sender.direction == .Right){
webView?.evaluateJavaScript("document.getElementByID('hplogo').style.backgroundColor='green';", completionHandler: nil)
}
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
webView?.evaluateJavaScript("document.getElementByID('hplogo').style.backgroundColor='red';", completionHandler: nil)
}
}
You misspelled the Javascript's function name. It should be getElementById instead of getElementByID:
document.getElementById('hplogo').style.backgroundColor = '...'