简体   繁体   中英

Custom webview keyboard issues

So adapting code form this thread UIKeyboardAppearance in UIWebView and TomSwift's awesome answer, I got about 99% of it working.

In the iOS 7 simulator, everything appears to work just fine. However in iOS 8, when the keyboard first appears, the < > Done bar is white. When I tap or select another input, it changes to my specified color.

My question is, how can I prevent and or change that white portion?

白条黑暗吧

All code in the other thread is identical, except for my color which I call like so in the keyboardWillAppear.

UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
    if (![[testWindow class] isEqual : [UIWindow class]]) {
        keyboardWindow = testWindow;
        break;
    }
}

// Locate UIWebFormView.
for (UIView *possibleFormView in [keyboardWindow subviews]) {
    if ([[possibleFormView description] hasPrefix : @"<UIInputSetContainerView"]) {

        for (UIView* peripheralView in possibleFormView.subviews) {
            peripheralView.backgroundColor = [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:0.75];

            for (UIView* peripheralView_sub in peripheralView.subviews) {
                peripheralView_sub.backgroundColor = [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:0.75];

            }
        }
    }
}

Any help would be greatly appreciated.

So with iOS 9+ out, I found it broke the mentioned methods. But with some tinkering and looking through some views, I came up with an addition to what i've already answered below.

Now I've decided to ditch the custom color stuff, I'm digging just the black keyboard, suits my app. Anyways, here's what works for me. Tested on 9.1 sim to 7. Also on my 6+ running 9.0.2.

//Keyboard setting
@interface UIWebBrowserView : UIView
@end
@interface UIWebBrowserView (UIWebBrowserView_Additions)
@end
@implementation UIWebBrowserView (UIWebBrowserView_Additions)
- (id)inputAccessoryView {
    return nil;
}

- (UIKeyboardAppearance) keyboardAppearance{

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    BOOL switchOn = [userDefaults boolForKey:@"darkKeyboard"];

    if (switchOn) {
        return UIKeyboardAppearanceDark;
    }
    else {
        return UIKeyboardAppearanceDefault;
    }
}
@end

@interface UITextInputTraits : UIWebBrowserView
@end
@interface UITextInputTraits (UIWebBrowserView)
@end
@implementation UITextInputTraits (UIWebBrowserView)
- (UIKeyboardAppearance) keyboardAppearance{

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    BOOL switchOn = [userDefaults boolForKey:@"darkKeyboard"];

    if (switchOn) {
        return UIKeyboardAppearanceDark;
    }
    else {
        return UIKeyboardAppearanceDefault;
    }
}
@end

I really hope somebody finds these answers helpful :D

UPDATED INFO: Was curious about the done bar, which is how this all got started. I re-enabled it just to see, and to find out it changed it to black. Nice bonus, although I've ditched it to hide the keyboard with scroll.

UPDATE 12/19/15 So I decided to make my transition from UIWebView to WKWebView, only to find out that obviously things are different between the two. I've managed to get it working again. Regular UIKeyboardAppearanceDark calls causes the keyboard to be more transparent than I like. So I modified it to my liking. Again, probably not the standard way of doing things, but I don't care, it's not going to apple anyways.

Everything is still being put in the AppDelegate.

//Removing the input bar above the keyboard.
@interface InputHider : NSObject @end
@implementation InputHider
-(id)inputAccessoryView{
    return nil;
}
@end

@interface UIWebBrowserView : NSObject
@end
@interface NSObject (UIWebBrowserView_Additions)
@end
@implementation NSObject (UIWebBrowserView_Additions)
- (UIKeyboardAppearance) keyboardAppearance{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    BOOL switchOn = [userDefaults boolForKey:@"darkKeyboard"];

    if (switchOn) {
        UIWindow *keyboardWindow = nil;
        for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
            if (![[testWindow class] isEqual : [UIWindow class]]) {
                keyboardWindow = testWindow;
                break;
            }
        }

        // Locate UIWebFormView.
        for (UIView *possibleFormView in [keyboardWindow subviews]) {

            if ([possibleFormView isKindOfClass:NSClassFromString(@"UIInputSetContainerView")] ||
                [possibleFormView isKindOfClass:NSClassFromString(@"UIInputSetHostView")]) {
                for (UIView* peripheralView in possibleFormView.subviews) {
                    peripheralView.backgroundColor = [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:1.0];

                    //Keyboard background
                    for (UIView* peripheralView_sub in peripheralView.subviews) {
                        peripheralView_sub.backgroundColor = [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:1.0];

                        //Accessory bar color
                        if ([possibleFormView isKindOfClass:NSClassFromString(@"WKContentView")]) {
                            for (UIView* UIInputViewContent_sub in peripheralView_sub.subviews) {
                                [[UIInputViewContent_sub layer] setOpacity : 1.0];
                                UIInputViewContent_sub.backgroundColor = [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:1.0];

                            }
                        }
                    }
                }
            }
        }
        return UIKeyboardAppearanceDark;
    }
    else {
        UIWindow *keyboardWindow = nil;
        for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
            if (![[testWindow class] isEqual : [UIWindow class]]) {
                keyboardWindow = testWindow;
                break;
            }
        }

        // Locate UIWebFormView.
        for (UIView *possibleFormView in [keyboardWindow subviews]) {

            if ([possibleFormView isKindOfClass:NSClassFromString(@"UIInputSetContainerView")] || [possibleFormView isKindOfClass:NSClassFromString(@"UIKeyboard")]) {
                for (UIView* peripheralView in possibleFormView.subviews) {
                    peripheralView.backgroundColor = [UIColor clearColor];

                    //Keyboard background
                    for (UIView* peripheralView_sub in peripheralView.subviews) {
                        peripheralView_sub.backgroundColor = [UIColor clearColor];

                        //Accessory bar color
                        if ([possibleFormView isKindOfClass:NSClassFromString(@"UIWebFormAccessory")]) {
                            for (UIView* UIInputViewContent_sub in peripheralView_sub.subviews) {
                                [[UIInputViewContent_sub layer] setOpacity : 1.0];
                                UIInputViewContent_sub.backgroundColor = [UIColor clearColor];

                            }
                        }
                    }
                }
            }
        }
        return UIKeyboardAppearanceDefault;
    }
}
@end

@interface UITextInputTraits : UIWebBrowserView
@end
@interface UITextInputTraits (UIWebBrowserView)
@end
@implementation UITextInputTraits (UIWebBrowserView)
- (UIKeyboardAppearance) keyboardAppearance{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    BOOL switchOn = [userDefaults boolForKey:@"darkKeyboard"];

    if (switchOn) {
        return UIKeyboardAppearanceDark;
    }
    else {
        return UIKeyboardAppearanceDefault;
    }
}
@end

//Disables endDisablingInterfaceAutorotationAnimated error for keyboard
@interface UIWindow (UIWebBrowserView)
- (void)beginDisablingInterfaceAutorotation;
- (void)endDisablingInterfaceAutorotation;
@end

@implementation UIWindow (UIWebBrowserView)
- (void)beginDisablingInterfaceAutorotation {}
- (void)endDisablingInterfaceAutorotation{}
@end

I haven't managed to find a way to hide the inputAccessoryBar like I did before, but thanks to a couple threads I got it working. In my view controllers I call:

-(void)removeInputAccessoryView {
    UIView* subview;

    for (UIView* view in webView.scrollView.subviews) {
        if([[view.class description] hasPrefix:@"WKContent"])
            subview = view;
    }

    if(subview == nil) return;

    NSString* name = [NSString stringWithFormat:@"%@SwizzleHelper", subview.class.superclass];
    Class newClass = NSClassFromString(name);

    if(newClass == nil)
    {
        newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0);
        if(!newClass) return;

        Method method = class_getInstanceMethod([AppDelegate class], @selector(inputAccessoryView));
        class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method));

        objc_registerClassPair(newClass);
    }

    object_setClass(subview, newClass);
}

And in the viewDidLoad I call:

[self removeInputAccessoryView];

I plan on tinkering around some more, but for now, this works to what I need it to do.

WKWebView Keyboard

Here is a solution for WKWebView's that uses swizzling, and is fairly easy to incorporate and works on iOS 9, 10 & 11. Just create a new class called WKKeyboard and add the following code:

WKKeyboard.h

#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>

@interface WKKeyboard : NSObject

+ (void)setStyle:(UIKeyboardAppearance)style on:(WKWebView *)webView;

@end

WKKeyboard.m

#import "WKKeyboard.h"
#import <objc/runtime.h>

@implementation WKKeyboard

// Allows the changing of keyboard styles
static UIKeyboardAppearance keyboardStyle;

// Leave this as an instance method
- (UIKeyboardAppearance)keyboardAppearance {
    return keyboardStyle;
}

// This can be a class method
+ (void)setStyle:(UIKeyboardAppearance)style on:(WKWebView *)webView {
    for (UIView *view in [[webView scrollView] subviews]) {
        if([[view.class description] containsString:@"WKContent"]) {
            UIView *content = view;
            NSString *className = [NSString stringWithFormat:@"%@_%@",[[content class] superclass],[self class]];
            Class newClass = NSClassFromString(className);
            if (!newClass) {
              newClass = objc_allocateClassPair([content class], [className cStringUsingEncoding:NSASCIIStringEncoding], 0);
              Method method = class_getInstanceMethod([WKKeyboard class], @selector(keyboardAppearance));
              class_addMethod(newClass, @selector(keyboardAppearance), method_getImplementation(method), method_getTypeEncoding(method));
              objc_registerClassPair(newClass);
            }
            object_setClass(content, newClass);
            keyboardStyle = style;
            return;
        }
    }
}

@end

Usage

// The WKWebView you want to change the keyboard on
WKWebView *webView = [WKWebView alloc] init];

// Then just call the class method with the style and webview
[WKKeyboard setStyle:UIKeyboardAppearanceDark on:webView];

Hope this helps someone, this way you can selectively change the appearance on a single WKWebView and not all of them!

While it would have been nice to get this working, I decided to take the approach of ditching it altogether, and instead, using the tap to dismiss the keyboard.

I was under the impression that hiding it would also hide the autocorrect bar as well. This is not the case.

Below is the full code I'm working with.

Final color code for keyboard, which is called by a toggle switch in my app settings.

NSUserDefaults *darkDefaults = [NSUserDefaults standardUserDefaults];
BOOL darkOn = [darkDefaults boolForKey:@"darkKeyboard"];

if (darkOn) {
    UIWindow *keyboardWindow = nil;
    for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
        if (![[testWindow class] isEqual : [UIWindow class]]) {
            keyboardWindow = testWindow;
            break;
        }
    }

    // Locate UIWebFormView.
    for (UIView *possibleFormView in [keyboardWindow subviews]) {

        if ([possibleFormView isKindOfClass:NSClassFromString(@"UIInputSetContainerView")]) {
            for (UIView* peripheralView in possibleFormView.subviews) {

                //Keyboard background
                for (UIView* peripheralView_sub in peripheralView.subviews) {
                    peripheralView_sub.backgroundColor = [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:0.75];
                }
            }
        }
    }
}
else{
    UIWindow *keyboardWindow = nil;
    for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
        if (![[testWindow class] isEqual : [UIWindow class]]) {
            keyboardWindow = testWindow;
            break;
        }
    }

    // Locate UIWebFormView.
    for (UIView *possibleFormView in [keyboardWindow subviews]) {

        if ([possibleFormView isKindOfClass:NSClassFromString(@"UIInputSetContainerView")]) {
            for (UIView* peripheralView in possibleFormView.subviews) {

                //Keyboard background
                for (UIView* peripheralView_sub in peripheralView.subviews) {
                    peripheralView_sub.backgroundColor = [UIColor clearColor];
                }
            }
        }
    }
}

To hide keyboard. Called at the top of my view controller (probably not apple safe, but I have no need to publish so it works for me):

@interface UIWebBrowserView : UIView
@end

@implementation UIWebBrowserView (CustomToolbar)
- (id)inputAccessoryView {
    return nil;
}
@end

Now from my testing, I was able to color it by drawing a new view or toolbar in the inputAccessoryView portion, but the tap to dismiss messes with it, some adjustment would be needed, but it's not the normal standard bar I was going for. Oh well.

If you wanted to implement the toggle as I have done from a table view, here's how I did that.

- (id)init{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(reloadTableView:)
                                                 name:NSUserDefaultsDidChangeNotification object:nil];

    return [self initWithStyle:UITableViewStyleGrouped];
}

cellForRowAtIndexPath:

                [cell.textLabel setText:@"Dark Keyboard"];
                cell.textLabel.textAlignment = NSTextAlignmentLeft;
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
                darkKeyboard = [[UISwitch alloc] initWithFrame: CGRectMake(7, 0, 0, 0)];
                cell.accessoryView = [[UIView alloc] initWithFrame:darkKeyboard.frame];
                [cell.accessoryView addSubview:darkKeyboard];
                [self.darkKeyboard addTarget:self action:@selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventValueChanged];

                //On Color
                darkKeyboard.onTintColor = [UIColor colorWithRed:0.204 green:0.667 blue:0.863 alpha:0.85];
                //Off Color
                darkKeyboard.backgroundColor = [UIColor colorWithRed:0.678 green:0.161 blue:0.188 alpha:0.75];
                darkKeyboard.TintColor = [UIColor clearColor];
                darkKeyboard.layer.cornerRadius = 16.0;
                //Risize
                darkKeyboard.transform = CGAffineTransformMakeScale(1.1, 1.1);
                //User defaults
                darkKeyboard.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"darkKeyboard"];

                UIView *keyboard = [[UIView alloc] initWithFrame:cell.frame];
                keyboard.backgroundColor = [UIColor colorWithRed:0.176 green:0.176 blue:0.176 alpha:1];
                cell.backgroundView = keyboard;

didSelectRowAtIndexPath: Just added a NSLog, nothing needs to go here.

- (void)updateSwitchAtIndexPath:(id)sender {

    if (darkKeyboard){
        NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        [userDefaults setBool:self.darkKeyboard.on forKey:@"darkKeyboard"];
        [userDefaults synchronize];

        if ([darkKeyboard isOn]) {
            [darkKeyboard setOn:YES animated:YES];
            [self.tableView reloadData];
            [[UIApplication sharedApplication] reloadInputViews];

        } else {
            [darkKeyboard setOn:NO animated:YES];
            [self.tableView reloadData];
            [[UIApplication sharedApplication] reloadInputViews];
        }
    }

}

Hope this helps somebody.

Extending UIWebBrowserView like the following, will get you're app banned from the app store.

@interface UIWebBrowserView : UIView
@end

@implementation UIWebBrowserView (KeyboardSwitch)
- (UIKeyboardAppearance) keyboardAppearance{
    return UIKeyboardAppearanceDark;
}
@end

So, you need to extend the web view during runtime.

The following code implements this in a way that is compatible with iOS12 and doesn't get rejected by Apple. For this example, I'm using a global _s_isDark to determine the required keyboard style.

@implementation UIWebView (KeyboardAppearanceAndAccessoryHiding)

- (void) setModifiedWebviewView {
    // find the UIWebBrowserView
    for (UIView *browserView in self.scrollView.subviews) {
        if ([NSStringFromClass([browserView class]) hasPrefix:@"UIWebBrowserView"]) {
            // Allocate a UIWebBrowserView subclass
            Class newClass = objc_allocateClassPair([browserView class], "UIWebBrowserModified", 0);

            // Add a nil method to hide the accessory view
            IMP nilImp = [self methodForSelector:@selector(methodReturningNil)];
            class_addMethod(newClass, @selector(inputAccessoryView), nilImp, "@@:");

            // Add a method to set dark or light keyboard
            Method m = class_getInstanceMethod([self class], @selector(keyboardAppearance));
            IMP keyboardAppearanceImp = method_getImplementation(m);
            const char* typeEncoding = method_getTypeEncoding(m);
            class_addMethod(newClass, @selector(keyboardAppearance), keyboardAppearanceImp, typeEncoding);

            // Replace the class of the UIWebBrowserView with the new subclass
            objc_registerClassPair(newClass);
            object_setClass(browserView, newClass);
            break;
        }
    }
}

- (id)methodReturningNil {
    return nil;
}

- (UIKeyboardAppearance)keyboardAppearance {
    return _s_isDark ? UIKeyboardAppearanceDark : UIKeyboardAppearanceLight;
}

@end

// We also need to extend the text input traits
@interface UITextInputTraits
@end
@interface UITextInputTraits (ForWebViewFields)
@end
@implementation UITextInputTraits (ForWebViewFields)
- (UIKeyboardAppearance)keyboardAppearance {
    return _s_isDark ? UIKeyboardAppearanceDark : UIKeyboardAppearanceLight;
}
@end

Swift version of @Asleepace's answer. And It just works out for me. Method swizzling is a way to go here.

class WKKeybaord: NSObject {
static var keyboardStyle: UIKeyboardAppearance = .default

@objc func keyboardAppearance() -> UIKeyboardAppearance {
    return WKKeybaord.keyboardStyle
}

class func setStyle(with style: UIKeyboardAppearance, on webView: WKWebView) {
    for view in webView.scrollView.subviews {
        if view.self.description.contains("WKContent") {
            let content = view
            var className: String? = nil
            if let superclass = content.self.superclass {
                className = "\(superclass)_\(type(of: self))"
            }
            var newClass: AnyClass? = NSClassFromString(className ?? "")
            if newClass == nil {
                newClass = objc_allocateClassPair(object_getClass(content), className ?? "", 0)
                if let method = class_getInstanceMethod(WKKeybaord.self, #selector(self.keyboardAppearance)) {
                    class_addMethod(newClass, #selector(self.keyboardAppearance), method_getImplementation(method), method_getTypeEncoding(method))
                    objc_registerClassPair(newClass!)
                }
            }
            object_setClass(content, newClass!)
            keyboardStyle = style
            return
        }
    }
}
}

Alternative answer.

So without trying to define another color, just simply getting a light or dark appearance with the switch that was mentioned. I added this to my AppDelegate, (some people don't like adding stuff here, but with so many views in my app this is a must).

Add this above the @interface AppDelegate ()

@interface UIWebBrowserView : UIView
@end

@implementation UIWebBrowserView (KeyboardSwitch)

- (UIKeyboardAppearance) keyboardAppearance{

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    BOOL switchOn = [userDefaults boolForKey:@"darkKeyboard"];

    if (switchOn) {
        return UIKeyboardAppearanceDark;
    }
    else {
        return UIKeyboardAppearanceDefault;
    }
}
@end

Works perfectly. Probably won't be accepted by Apple, but I have no interest in that.

Now for people that don't want to create a toggle and just want the dark keyboard.

@interface UIWebBrowserView : UIView
@end

@implementation UIWebBrowserView (KeyboardSwitch)

- (UIKeyboardAppearance) keyboardAppearance{

    return UIKeyboardAppearanceDark;
}
@end

UPDATE: With custom color: Tested from iOS 7-8.2 in simulator.

- (UIKeyboardAppearance) keyboardAppearance{

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    BOOL switchOn = [userDefaults boolForKey:@"darkKeyboard"];

    if (switchOn) {
        UIWindow *keyboardWindow = nil;
        for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
            if (![[testWindow class] isEqual : [UIWindow class]]) {
                keyboardWindow = testWindow;
                break;
            }
        }

        for (UIView *possibleFormView in [keyboardWindow subviews]) {
            if ([possibleFormView isKindOfClass:NSClassFromString(@"UIInputSetContainerView")] || [possibleFormView isKindOfClass:NSClassFromString(@"UIPeripheralHostView")]) {
                for (UIView* peripheralView in possibleFormView.subviews) {

                    //Keyboard background
                    for (UIView* peripheralView_sub in peripheralView.subviews) {
                        //Setting custom color
                        peripheralView_sub.backgroundColor = [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:0.50];
                    }
                }
            }
        }
        return UIKeyboardAppearanceDark;
    }
    else {
        UIWindow *keyboardWindow = nil;
        for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
            if (![[testWindow class] isEqual : [UIWindow class]]) {
                keyboardWindow = testWindow;
                break;
            }
        }

        for (UIView *possibleFormView in [keyboardWindow subviews]) {
            if ([possibleFormView isKindOfClass:NSClassFromString(@"UIInputSetContainerView")] || [possibleFormView isKindOfClass:NSClassFromString(@"UIPeripheralHostView")]) {
                for (UIView* peripheralView in possibleFormView.subviews) {

                    //Keyboard background
                    for (UIView* peripheralView_sub in peripheralView.subviews) {
                        //Clear color so it doesn't show when switching with toggle
                        peripheralView_sub.backgroundColor = [UIColor clearColor];
                    }
                }
            }
        }
        return UIKeyboardAppearanceDefault;
    }
}
@end

Hope this helps future webview developers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM