坊やがゆく

iPhoneアプリ

2011-08-26

【iPhone】任意のデザインにタブバーをカスタマイズする

iタウンページアプリみたいなタブバー

f:id:PoohKid:20110806104350p:image

こんなのを作りたいので色々試してみる。


その1

継承やカテゴリで背景や選択/非選択画像を指定する。

@implementation UITabBar (UITabBarItem_ColorImage)
//背景描画
- (id)initWithCoder:(NSCoder *)aDecoder {
   if (self = [super initWithCoder:aDecoder]) {
       //背景をグラディエーションにする
       CAGradientLayer *gradientLayer = [[[CAGradientLayer alloc] init] autorelease];
       [gradientLayer setBounds:self.bounds];
       [gradientLayer setPosition:CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2)];
       [gradientLayer setColors:
        [NSArray arrayWithObjects:
         (id)[[UIColor colorWithRed:102 green:204 blue:255 alpha:1.0] CGColor],
         (id)[[UIColor colorWithRed:0 green:0 blue:128 alpha:1.0] CGColor],
         nil]];
       [self.layer insertSublayer:gradientLayer atIndex:0];
   }
   return self;
}
@end

@implementation UITabBarItem (UITabBarItem_ColorImage)
//アイコン部分の画像のみ変わる(選択状態)
- (UIImage *)selectedImage
{
   return [UIImage imageNamed:@"selected.png"];
}
//アイコン部分の画像のみ変わる(非選択状態)
- (UIImage *)unselectedImage
{
   return [UIImage imageNamed:@"unselected.png"];
}
@end

ただしこれだとUITabBar規定の「選択時にマスクをかける」動作に邪魔され希望するデザインにならない。


その2

デザインはすべて1枚絵で表現する。ボタンを押したパターン数用意する。

※iOS5の時点でこの方式は動作しませんでした。

Tabの遷移を受け取り、insertSubViewでaddしたViewの画像を逐一取り替えるという荒技を試してみました。

http://tettasun.sakura.ne.jp/blog/?p=128

f:id:PoohKid:20110806111034p:image

自由にデザインできるためデザイン性を損なわず、プログラム側で無理して色をつけたり画像を差し替える必要もない。

GitHub

https://github.com/PoohKid/FreeDesignedTabBar


その3

上に任意のViewを重ねる。

機能をのっとる系

コンポーネントの上にUIView/UIButtonを貼付け、delegateの内容をそのままもらって描画

iPhoneのUIのスキンをカスタマイズする場合のメモ | fladdict

f:id:PoohKid:20110806111035p:image

デザインの自由度が高く実装も楽。

コードで無理に実装するより効果的な例とのこと(先生談)。

GitHub

https://github.com/PoohKid/FreeDesignedTabBar2


その4

研修の同期にFireworks苦手だからと全部コードで製作するというツワモノがいました。

  • UITabBarのLayerはCGContextDrawLinearGradientでグラデーション描画
  • UITabBarItemはカスタマイズしたものを配置
  • selectedImageとunselectedImageは[super image]を元にマスクを生成して好きな色の上に重ねて表現
  • CGContextSetShadowで影も付けるよ
- (UIImage *)selectedImage
{
    CGImageRef maskRef = [super image].CGImage;

    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
                                        CGImageGetHeight(maskRef),
                                        CGImageGetBitsPerComponent(maskRef),
                                        CGImageGetBitsPerPixel(maskRef),
                                        CGImageGetBytesPerRow(maskRef),
                                        CGImageGetDataProvider(maskRef), NULL, false);
    CGSize size = CGSizeMake(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef));
    UIGraphicsBeginImageContext(size);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGColorRef halationTop = [UIColor colorWithRed:255.0/255.0 
                                             green:255.0/255.0 
                                              blue:255.0/255.0 
                                             alpha:1.0].CGColor;
    CGColorRef halationBottom = [UIColor colorWithRed:255.0/255.0 
                                                green:206.0/255.0 
                                                 blue:4.0/255.0 
                                                alpha:1.0].CGColor;
    CGColorRef normalTop = [UIColor colorWithRed:251.0/255.0 
                                           green:199.0/255.0 
                                            blue:2.0/255.0 
                                           alpha:1.0].CGColor;
    CGColorRef normalBottom = [UIColor colorWithRed:251.0/255.0 
                                              green:199.0/255.0 
                                               blue:2.0/255.0 
                                              alpha:1.0].CGColor;
    NSArray* colors = [NSArray arrayWithObjects:
                       (id)halationTop, 
                       (id)halationBottom, 
                       (id)normalTop, 
                       (id)normalBottom, nil];
    CGFloat locations[] = {0.0, 0.5, 0.5, 1.0};
    CGGradientRef gradient = CGGradientCreateWithColors(CGColorGetColorSpace(halationTop), 
                                                        (CFArrayRef)colors, locations);
    CGPoint gradientStartPoint = CGPointZero;
    CGPoint gradientEndPoint = CGPointMake(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef));
    CGContextDrawLinearGradient(ctx, 
                                gradient, 
                                gradientStartPoint, 
                                gradientEndPoint, 
                                kCGGradientDrawsAfterEndLocation);
    CGGradientRelease(gradient);
    CGImageRef image = CGBitmapContextCreateImage(ctx);
    UIGraphicsEndImageContext();

    CGImageRef masked = CGImageCreateWithMask(image, mask);
    CGImageRelease(image);
    CGImageGetWidth(mask);

    // add shadow
    UIGraphicsBeginImageContext(size);
    ctx = UIGraphicsGetCurrentContext();
    CGSize myShadowOffset = CGSizeMake (3.0,  3.0);    
    CGContextSaveGState(ctx);
    CGContextSetShadow (ctx, myShadowOffset, 0);
    CGContextTranslateCTM(ctx, 0, size.height);
    CGContextScaleCTM(ctx, 1.0, -1.0);
    CGContextDrawImage(ctx, CGRectMake(0.0, 0.0, size.width, size.height), masked);
    CGContextRestoreGState(ctx);

    CGImageRef shadowed = CGBitmapContextCreateImage(ctx);
    UIGraphicsEndImageContext();

    UIImage *ui_image = [UIImage imageWithCGImage:shadowed];
    CGImageRelease(masked);
    CGImageRelease(shadowed);

    return ui_image;
}
  • タブバーが選択された時の挙動はsublayersの値をキー値監視して乗っ取り、影を描画
    UIView *v = [tb.subviews objectAtIndex:0];
    [v.layer addObserver:self forKeyPath:@"sublayers" options:NSKeyValueObservingOptionNew context:nil];
    v = [tb.subviews objectAtIndex:1];
    [v.layer addObserver:self forKeyPath:@"sublayers" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSArray *array = [object valueForKey:@"sublayers"];
    if ([array count] == 3) {
        CALayer *layer = [array objectAtIndex:0];
        layer.backgroundColor = [UIColor blackColor].CGColor;
        layer.opacity = 0.5;
        layer.cornerRadius = 5.0;
    }
}

なんというハック力w

現在フリーなのでスカウトするなら今がチャンスですよ〜!