David Hoerl
David Hoerl

Reputation: 41642

How to create UITextField accessory view iOS7 with appropriate opacity, or even duplicate the keys?

In iOS7 Apple made the keyboard translucent (on iPhone). If I want to add an acceossory view matching the opacity, how do I do this? Also, I'd like to mimic the keyboard keys in my accessory view - how would I do that?

Upvotes: 2

Views: 2006

Answers (2)

Jonathan M
Jonathan M

Reputation: 1889

Wouldn't UIInputView be of any help in this case ?

The Apple Class Reference says the following :

The UIInputView class is designed to match the appearance of the standard system keyboard when used as an input view with a responder. When defining your own custom input views or input accessory views, you can use a UIInputView object as the root view and add any subviews you want to create your input view. The input view and its subviews receive tinting and blur effects based on the options you specify at initialization time.

Upvotes: 8

David Hoerl
David Hoerl

Reputation: 41642

On iOS7, the phone pops a translucent keyboard, but the accessoryView is presented as is - no transluceny applied. So if a user has a view under the keypad, the user will see some of bleeding through the keyboard, but not your accessory view. How can I determine the appropriate opacity to apply there?

The way to do it is to measure the background changes when the keyboard pops over a white background, and then black one. But evaluating the changes in the keyboard view, you can figure out what the keyboard is using for a background color - the color and opacity setting. Ditto for the keys and their borders etc.

So that's what the following code does - but it does even more. It parameterizes the buttons - for iPhone, iPad, darkKeyboard (new for iOS7) and the lightKeyboard.

If your want to replicate the buttons for either device, either color, you can do that - to add to your accessory view.

In case you don't want to be bothered creating our own project from the code, your can get one on github - search on CreateShadowedRoundRectButtonImage - but if for some reason that goes away the code is follows.

EDIT: note that after some testing, I found that without blur the opacity of the view allows text underneath it to show clearly. Right now looking to use the suggested background color below with opacity set to 0.8 as the barTintColor (with translucency=YES) in a UIToolbar (as an inputAccessoryView)

typedef struct { CGFloat whiteColor ; CGFloat opacity; } setting;

typedef struct {
    CGFloat     keyboardBackColor;
    CGFloat     keyBackColor;
    CGFloat     keyShadowColor;
    //CGFloat   keyFontColor;       // not changing in 7.0

    CGFloat     altKeyBackColor;
    CGFloat     altKeyShadowColor;
    //CGFloat   altKeyFontColor;    // not changing in 7.0
} keyboardVariant;

typedef struct {
    setting     keyboardBackSetting;
    setting     keyBackColorSetting;
    setting     keyShadowColorSetting;

    setting     altKeyBackColorSetting;
    setting     altKeyShadowColorSetting;

} settings;

static settings whiteKeyboardSettings, blackKeyboardSettings;

static BOOL isIdiomPhone;

@implementation CreateButton

+ (void)initialize
{
    isIdiomPhone = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone;

    // Using 'Pixie' observe values of the keyboard with an all white or all black background, to compute white and opacity
    // NOTE: black values must be somewhat smaller than the light ones or you get divide by null
    if(isIdiomPhone) {
        // White Keyboard
        {
            keyboardVariant white = { .87f, .99f, .53f,   .77f, .53f };
            keyboardVariant black = { .65f, .98f, .40f,   .66f, .40f }; // Alt Font was .39 no opacity
            whiteKeyboardSettings = [self defineWithWhiteBackground:white blackBackground:black];
        }
        // Black Keyboard
        {
            keyboardVariant white = { .36f, .55f, .21f,   .42f, .21f };
            keyboardVariant black = { .08f, .35f, .05f,   .20f, .05f }; // Alt Font 1.0
            blackKeyboardSettings = [self defineWithWhiteBackground:white blackBackground:black];
        }
    } else {
        // White Keyboard
        {
            keyboardVariant white = { .82f, .99f, .49f,   .75f, .49f };
            keyboardVariant black = { .81f, .98f, .48f,   .74f, .48f }; // Alt Font 1.0
            whiteKeyboardSettings = [self defineWithWhiteBackground:white blackBackground:black];
        }
        // Black Keyboard
        {
            keyboardVariant white = { .05f, .34f, .03f,   .19f, .03f };
            keyboardVariant black = { .04f, .33f, .02f,   .18f, .02f }; // Alt Font 1.0
            blackKeyboardSettings = [self defineWithWhiteBackground:white blackBackground:black];
        }
    }
}

+ (UIColor *)backgroundColorForType:(UIKeyboardAppearance)type
{
    setting s = type == UIKeyboardAppearanceLight ? whiteKeyboardSettings.keyboardBackSetting : blackKeyboardSettings.keyboardBackSetting;
    return  [UIColor colorWithRed:s.whiteColor-.01 green:s.whiteColor blue:s.whiteColor+.01 alpha:s.opacity];
}

+ (settings)defineWithWhiteBackground:(keyboardVariant)white blackBackground:(keyboardVariant)black
{
    settings v;

    v.keyboardBackSetting       = [self solveForWhite:white.keyboardBackColor black:black.keyboardBackColor whiteBG:1 blackBG:0];
    v.keyBackColorSetting       = [self solveForWhite:white.keyBackColor black:black.keyBackColor whiteBG:white.keyboardBackColor blackBG:black.keyboardBackColor];
    v.keyShadowColorSetting     = [self solveForWhite:white.keyShadowColor black:black.keyShadowColor whiteBG:white.keyboardBackColor blackBG:black.keyboardBackColor];

    v.altKeyBackColorSetting    = [self solveForWhite:white.altKeyBackColor black:black.altKeyBackColor whiteBG:white.keyboardBackColor blackBG:black.keyboardBackColor];
    v.altKeyShadowColorSetting  = [self solveForWhite:white.altKeyShadowColor black:black.altKeyShadowColor whiteBG:white.keyboardBackColor blackBG:black.keyboardBackColor];

    return v;
}

+ (setting)solveForWhite:(CGFloat)white black:(CGFloat)black whiteBG:(CGFloat)wBG blackBG:(CGFloat)bBG
{
    // Solve two equations in two variables:
    //   bBG(1-opacity) + whiteColor*opacity = black
    //   wBG(1-opacity) + whiteColor*opacity = white

    if(isIdiomPhone) {
        CGFloat a = black - bBG;
        CGFloat b = white - wBG;

        CGFloat colorDiff = (a - b);
        CGFloat backgDiff = (wBG - bBG);

        CGFloat opacity = colorDiff / backgDiff;
        opacity = MIN(1, opacity);
        opacity = MAX(0, opacity);

        CGFloat whiteColor = b/opacity + wBG;
        whiteColor = MIN(1, whiteColor);
        whiteColor = MAX(0, whiteColor);
        assert(whiteColor);

        setting setting;
        setting.opacity = opacity;
        setting.whiteColor = whiteColor;

        return setting;
    } else {
        setting setting;
        setting.opacity = 1;
        setting.whiteColor = white;
        return setting;
    }
}

- (UIImage *)buttonImage:(CGSize)size type:(UIKeyboardAppearance)type
{
    return [self buttonImage:size type:type isAlt:NO];
}

- (UIImage *)altButtonImage:(CGSize)size type:(UIKeyboardAppearance)type
{
    return [self buttonImage:size type:type isAlt:YES];
}

- (UIImage *)buttonImage:(CGSize)size type:(UIKeyboardAppearance)type isAlt:(BOOL)altButton
{
    CGFloat cornerRadius = isIdiomPhone ? 4 : 7;
    setting foreSetting, shadowSetting;

    if(altButton) {
        foreSetting   = type == UIKeyboardAppearanceLight ? whiteKeyboardSettings.altKeyBackColorSetting : blackKeyboardSettings.altKeyBackColorSetting;
        shadowSetting = type == UIKeyboardAppearanceLight ? whiteKeyboardSettings.altKeyShadowColorSetting : blackKeyboardSettings.altKeyShadowColorSetting;
    } else {
        foreSetting   = type == UIKeyboardAppearanceLight ? whiteKeyboardSettings.keyBackColorSetting : blackKeyboardSettings.keyBackColorSetting;
        shadowSetting = type == UIKeyboardAppearanceLight ? whiteKeyboardSettings.keyShadowColorSetting : blackKeyboardSettings.keyShadowColorSetting;
    }

    UIColor *foregroundColor    = [UIColor colorWithRed:foreSetting.whiteColor-.01 green:foreSetting.whiteColor blue:foreSetting.whiteColor+.01 alpha:foreSetting.opacity];
    UIColor *shadowColor        = [UIColor colorWithRed:shadowSetting.whiteColor-.01 green:shadowSetting.whiteColor blue:shadowSetting.whiteColor+.01 alpha:shadowSetting.opacity];

    CGRect frame = (CGRect){ {0,0}, size };

    UIGraphicsBeginImageContextWithOptions(size, NO, 0);    // NO -> not opaque

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeCopy);

    if(isIdiomPhone && altButton) {
        UIColor *altColor = [UIColor colorWithRed:foreSetting.whiteColor+.05 green:foreSetting.whiteColor+.06 blue:foreSetting.whiteColor+.07 alpha:foreSetting.opacity];
        [self drawRoundRectofSize:frame inContext:context color:altColor radius:cornerRadius];
        frame.size.height -= 1;
        frame.origin.y += 1;
        [self drawRoundRectofSize:frame inContext:context color:shadowColor radius:cornerRadius];
    } else {
        [self drawRoundRectofSize:frame inContext:context color:shadowColor radius:cornerRadius];
    }
    frame.size.height -= 1;
    [self drawRoundRectofSize:frame inContext:context color:foregroundColor radius:cornerRadius];

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return image;
}


- (void)drawRoundRectofSize:(CGRect)rect inContext:(CGContextRef)context color:(UIColor *)fillColor radius:(CGFloat)radius
{
    CGContextSetFillColorWithColor(context, fillColor.CGColor);

    CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
    CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, 
                radius, M_PI, M_PI / 2, 1); //STS fixed
    CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, 
                        rect.origin.y + rect.size.height);
    CGContextAddArc(context, rect.origin.x + rect.size.width - radius, 
                rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
    CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, 
                radius, 0.0f, -M_PI / 2, 1);
    CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius, 
                -M_PI / 2, M_PI, 1);

    CGContextFillPath(context);
}

Upvotes: 4

Related Questions