Viewにグラデーションレイヤを適用する その2

やはりアドホックな対応というのは無理があるようだ。さて、どうしようか。

きちんと再描画をさせるのであればやはりUIViewの派生クラスを書かなければ駄目だろう。ということで実際に書いてみた。

実装
    • BBUIGradientView.h
#import <UIKit/UIKit.h>

//グラデーションのアングル
enum BBUIGradientViewAngle
{
    ANGLE_0 = 0,
    ANGLE_45 = 45,
    ANGLE_90 = 90,
    ANGLE_135 = 135,
    ANGLE_180 = 180,
    ANGLE_225 = 225,
    ANGLE_270 = 270
};

@interface BBUIGradientView : UIView

@property (nonatomic, strong, setter = setStartColor:) UIColor* startColor;
@property (nonatomic, strong, setter = setEndColor:) UIColor* endColor;
@property (nonatomic, setter = setAngle:) enum BBUIGradientViewAngle angle;

@end
    • BBUIGradientView.m
#import <QuartzCore/QuartzCore.h>

#import "BBUIGradientView.h"

@interface BBUIGradientView ()
{
    CAGradientLayer* _gradient;
}
@end
@implementation BBUIGradientView

- (void)setStartColor:(UIColor *)startColor
{
    _startColor = startColor;
    _gradient.colors = [NSArray arrayWithObjects:
                       (id)[_startColor CGColor], //開始色
                       (id)[_endColor CGColor], //終了色
                       nil];
    [self setNeedsDisplay];
}
- (void)setEndColor:(UIColor *)endColor
{
    _endColor = endColor;
    _gradient.colors = [NSArray arrayWithObjects:
                        (id)[_startColor CGColor], //開始色
                        (id)[_endColor CGColor], //終了色
                        nil];
    [self setNeedsDisplay];
}
- (void)setAngle:(enum BBUIGradientViewAngle)angle
{
    switch (angle)
    {
        case ANGLE_0:
        {
                    
            [_gradient setStartPoint:CGPointMake(0.5, 0.0)];
            [_gradient setEndPoint:CGPointMake(0.5, 1.0)]; 
            break;
        }
        case ANGLE_45:
        {
            [_gradient setStartPoint:CGPointMake(1.0, 0.0)];
            [_gradient setEndPoint:CGPointMake(0.0, 1.0)]; 
            break;
        }
        case ANGLE_90:
        {
            [_gradient setStartPoint:CGPointMake(1.0, 0.5)];
            [_gradient setEndPoint:CGPointMake(0.0, 0.5)];
            break;
        }
        case ANGLE_135:
        {
            [_gradient setStartPoint:CGPointMake(1.0, 1.0)];
            [_gradient setEndPoint:CGPointMake(0.0, 0.0)];
            break;
        }
        case ANGLE_180:
        {
            [_gradient setStartPoint:CGPointMake(0.5, 1.0)];
            [_gradient setEndPoint:CGPointMake(0.5, 0.0)]; 
            break;
        }
        case ANGLE_225:
        {
            [_gradient setStartPoint:CGPointMake(0.0, 1.0)];
            [_gradient setEndPoint:CGPointMake(1.0, 0.0)]; 
            break;
        }
        case ANGLE_270:
        {
            [_gradient setStartPoint:CGPointMake(0.0, 0.5)];
            [_gradient setEndPoint:CGPointMake(1.0, 0.5)];
            break;
        }
            
        default:
            break;
    }
    [self setNeedsDisplay];
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self)
    {
        //グラデーションレイヤの初期化
        _gradient = [CAGradientLayer layer];
        _gradient.frame = self.bounds;
        _gradient.colors = [NSArray arrayWithObjects:
                           (id)[[UIColor redColor] CGColor], //開始色
                           (id)[[UIColor blueColor] CGColor], //終了色
                           nil];
        
        self.angle = ANGLE_0;
        [self.layer insertSublayer:_gradient atIndex:0];
    }
    return self;
}

- (void)dealloc
{
    [_gradient removeFromSuperlayer];
    _gradient = nil;
    _startColor = nil;
    _endColor = nil;
}
- (void)layoutSubviews
{
    [super layoutSubviews];
    _gradient.frame = self.bounds;
}
@end

プロパティとしてグラデーションの角度は使いやすいように45度間隔を定数として与えられるようにしたが、それをSrartPointとEndPointに変換している部分がベタなコードで気に入らない。三角関数等を使ってもっとスマートな設定にしたいのだが良い方法を思いつかなかった。

使い方

InterfaceBuilderでは通常のUIViewとして配置して、その後Identitiy Inspectorでクラス名をUIViewからBBUIGradientViewにすれば良い。あとはIBOutletで接続して、コードで初期化する。

@property (weak, nonatomic) IBOutlet BBUIGradientView *pnlGradent;
:
:
- (void)viewDidLoad
{
    [super viewDidLoad];

    //ビュー背景のグラデーション
    pnlGradent.startColor = [UIColor yellowColor];
    pnlGradent.endColor = [UIColor greenColor];
    pnlGradent.angle = ANGLE_45;
   :
実行結果


今度はローテートしてもきちんと再描画される。

Xcode 4.4.1、iPhone 5.1シミュレータおよびiPhone 4Sで動くことを確認している。なお、私はARC以降iOSの世界に入ったので断りが無い場合、全てARCオンでコードを書いている。