Search code examples
iosswiftuiwebviewkeyboardinputaccessoryview

Replace inputAccessoryView of keyboard in UIWebView in Swift


I've been looking for days for a simple answer but I cannot seem to find it.

I want to find and replace the inputAccessoryView of keyboard attached to my UIWebView if the accessory view exists on the keyboard. I want to replace it so I can have my own Prev and Next buttons. Doing it in swift is a plus.

Sounds simple enough, but it's not obvious.


Solution

  • import UIKit
    
    extension UIViewController {
    
        func addNewAccessoryView(oldAccessoryView:UIView)
        {
            var frame = oldAccessoryView.frame;
            var newAccessoryView = UIView(frame:frame)
            newAccessoryView.backgroundColor = UIColor.lightGrayColor()
    
            let fn = ".HelveticaNeueInterface-Bold"
            var doneButton = UIButton(frame:CGRectMake(frame.size.width-80,6,100,30))
            doneButton.setTitle("Done", forState: UIControlState.Normal)
            doneButton.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
            doneButton.titleLabel!.font = UIFont(name: fn, size: 15)
            doneButton.addTarget(self, action: "buttonAccessoryDoneAction:", forControlEvents: UIControlEvents.TouchUpInside)
    
            var prevButton = UIButton(frame:CGRectMake(2,6,50,30))
            prevButton.setTitle("<", forState: UIControlState.Normal)
            prevButton.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
            prevButton.titleLabel!.font = UIFont(name: fn, size: 15)
            prevButton.addTarget(self, action: "buttonAccessoryPrevAction:", forControlEvents: UIControlEvents.TouchUpInside)
    
            var nextButton = UIButton(frame:CGRectMake(35,6,50,30))
            nextButton.setTitle(">", forState: UIControlState.Normal)
            nextButton.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
            nextButton.titleLabel!.font = UIFont(name: fn, size: 15)
            nextButton.addTarget(self, action: "buttonAccessoryNextAction:", forControlEvents: UIControlEvents.TouchUpInside)
    
            newAccessoryView.addSubview(prevButton);
            newAccessoryView.addSubview(nextButton);
            newAccessoryView.addSubview(doneButton);
    
            oldAccessoryView.addSubview(newAccessoryView)
    
        }
    
        func traverseSubViews(vw:UIView) -> UIView
        {
            if (vw.description.hasPrefix("<UIWebFormAccessory")) {
                return vw
            }
    
            for(var i = 0 ; i < vw.subviews.count; i++) {
                var subview = vw.subviews[i] as UIView;
                if (subview.subviews.count > 0) {
                    var subvw = self.traverseSubViews(subview)
                    if (subvw.description.hasPrefix("<UIWebFormAccessory")) {
                        return subvw
                    }
                }
            }
            return UIView()
        }
    
        func replaceKeyboardInputAccessoryView()
        {
            // locate accessory view
            let windowCount = UIApplication.sharedApplication().windows.count
            if (windowCount < 2) {
                return;
            }
    
            let tempWindow:UIWindow = UIApplication.sharedApplication().windows[1] as UIWindow
            var accessoryView:UIView = traverseSubViews(tempWindow)
            if (accessoryView.description.hasPrefix("<UIWebFormAccessory")) {
                // Found the inputAccessoryView UIView
                if (accessoryView.subviews.count > 0) {
                    self.addNewAccessoryView(accessoryView)
                }
            }
        }
    
        // Override these in your UIViewController
        func buttonAccessoryNextAction(sender:UIButton!)
        {
            println("Next Clicked")
        }
    
        func buttonAccessoryPrevAction(sender:UIButton!)
        {
            println("Prev Clicked")
        }
    
        func buttonAccessoryDoneAction(sender:UIButton!)
        {
            println("Done Clicked")
        }        
    }
    

    Then from inside your UIViewController setup a Notification when the keyboard appears. You can do this inside of viewDidAppear().

        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidShow:"), name:UIKeyboardDidShowNotification, object: nil);
    

    and from within your keyboardDidShow handler call the replaceKeyboardInputAccessoryView method of the UIViewController.

    func keyboardDidShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            self.replaceKeyboardInputAccessoryView()
        }
    }