【クリスマスだし】UISwtich/UISegmentedControlライクなカスタムコントロール「SVSegmentedControl」【25日目の2】

2012.12.25

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ios-SVSegmentedControl-0

ラベルやデザインの変更が容易でないことや、イベント発生条件がシビアなことで有名なUISwitchとUISegmentedControlですが、これらのコンポーネントをより使いやすくした感じのカスタムコントロールSVSegmentedControlをご紹介します!

おさらい

UISwitch

UISwitch

UISwitchは設定画面などで良く目にするコントロールの1つですが、超簡単に言うとタップしやすいチェックボタンです。表示されるラベルはオン/オフの2種類で、端末やアプリの言語設定に応じて言語が変わります。ちなみに、端末とアプリで言語設定が異なるとI/O表記になるそうです。

ラベル変更がむずい

表示されるラベルを変更するのが容易ではありません。面倒くさいです。よく紹介されているのは、UISwitchのサブビューに直接アクセスして変更する方法やサブクラスを作成して変更する方法などがありますが、公式のやり方ではないためiOSのバージョンが上がると使えなくなる可能性があります。きっとこのコントロールはオン/オフの切り替えのみに使うべきなんでしょう。

UISegmentedControl

UISegmentedControl

UISegmentedControlは画面表示の切り替えなどで良く目にしますね。これも超簡単に言うとタップしやすいラジオボタンです。

生成後のラベル変更がむずい

UISwitchと違って、ラベルはインスタンス生成時に文字列の配列で指定します。が、いったん生成すると、あとから変更することが困難です。これもサブビューにアクセスするなどして解決することができますが、UISwitchと同様iOSのバージョンが上がると使えなくなる可能性があります。

UISwitch/UISegmentedControlの両方で使いにくいところ

デザインの変更が難しい

上記のようにラベルの変更だけでもくせがあるのですが、デザインを変更するとなるとより大変です。

ハンドリングできるイベントが少ない

両方とも主にUIControlEventValueChangedイベントをハンドリングできるようになっています。このイベントは値が変わったときに呼ばれるイベントなので、値が変わらなければ呼ばれません。言い換えれば、選択中の値をもう一度選択しても呼ばれません。 かといってタッチ系のイベントをハンドリングするにはサブクラスを作らなければなりません。

そこでSVSegmentedControl!!

というわけで、本題のSVSegmentedControlの登場です。これを使うと上で記したような煩わしい部分を一挙に解決してくれます!!

早速使ってみよう!!

ここからは以下の環境で説明します。

Mac OS X 10.8 Moutain lion
Xcode 4.5.2
iOS SDK 6.0

まずはダウンロード

早速ダウンロードしましょう。SVSegmentedControlはGitHubで公開されています。
samvermette/SVSegmentedControl

サンプルプロジェクトを作成

まずはいつものようにサンプルプロジェクトを作成しましょう。XcodeよりSingle View Applicationを選択し、以下の内容でプロジェクトを作成しましょう。尚、今回はサンプルですので、保存の際にCreate local git repository for this projectはチェックを外しておきましょう。

項目 設定値
Product Name SVSegmentedControlSample
Organization Name 自分の名前(サンプルなのでテキトー)
Company Identifier 会社名(サンプルなのでテキトー)
Class Prefix なし
Devices iPhone
Use Storyboards チェックする(ストーリーボードを使用)
Use Automatic Reference Counting チェックする(ARC有効)
Include Unit Tests チェックしない(unit testのターゲットを含まない)

インポート

プロジェクトナビゲータにあるプロジェクトを右クリックし、「Add Files to "SVSegmentedControlSample"...」をクリックして、先ほどダウンロードしてできたディレクトリの中にSVSegmentedControlディレクトリがあるので、そのディレクトリを選択してインポートします。インポート後は以下のようになります。

SVSegmentedControlをプロジェクトに追加したあと

ちなみに、SVSegmentedControlはARCに対応しているので、ARCを無効にする場合はSVSegmentedControl.mとSVSegmentedThumb.mに-fobjc-arcを指定してださい。

QuartzCore.frameworkの追加

SVSegmentedControlではQuartzCore.frameworkを使用するので追加しておきましょう。

サンプルソース

ViewController.mを以下のように修正してください。

ViewController.m
#import "ViewController.h"
#import "SVSegmentedControl.h"

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 表示する項目の配列を指定してSVSegmentedControlインスタンス生成
    SVSegmentedControl *navSC = [[SVSegmentedControl alloc] initWithSectionTitles:@[@"First", @"Second", @"Third"]];
    
    // 値変更時のイベントハンドラをBlocksで指定する
    navSC.changeHandler = ^(NSUInteger newIndex) {
        NSLog(@"[%@]が選択されました。", navSC.sectionTitles[newIndex]);
    };
    
    // ビューに追加
	[self.view addSubview:navSC];
	
    // ビューの真ん中に表示
	navSC.center = self.view.center;
}

@end

ViewController.mを修正したら、早速Runしてみましょう。

実行例

SVSegmentedControl実行例

導入は簡単ですね。

SVSegmentedControlの構成

カスタマイズ方法を説明する前に、SVSegmentedControlの構成を理解しましょう。

SVSegmentedControlのパーツを簡単に説明します。

SVSegmentedControlの構成

上の図の通りSVSegmentedControlは、

SVSegmentedControlの構成
パーツ 説明 関連ファイル
SVSegmentedControl 本体
  • SVSegmentedControl/SVSegmentedControl.h
  • SVSegmentedControl/SVSegmentedControl.m
SVSegmentedThumb 選択中を表す部品
  • SVSegmentedControl/SVSegmentedThumb.h
  • SVSegmentedControl/SVSegmentedThumb.m

と、選択中を表す部品が別に定義されています。これにより詳細なデザインの調整が可能となっています。

SVSegmentedControlのカスタマイズ

指定できるプロパティ

SVSegmentedControlは、SVSegmentedControlとSVSegmentedThumbを分けて設定できます。まずは本体であるSVSegmentedControlのプロパティを見てみましょう。

SVSegmentedControlのプロパティ
プロパティ 説明 デフォルト値
sectionTitles NSArray 設定したセクションラベルを配列で参照できます。readonlyではないので動的に変更できそうですが、インスタンス生成後にセクション数を増減することはできないようです。
sectionImages NSArray セクションラベルの左側に表示する画像を設定します。画像は配列で指定し、同じインデックスのセクションラベルの横に表示されます。画像はUIBarButtonのときと同様、画像のアルファチャネルを利用して表示されます。
selectedIndex NSUInteger 現在選択中のラベルのインデックスを取得/設定します。 0
crossFadeLabelsOnDrag BOOL SVSegmentedThumbが選択やドラッグにより移動するときに、ラベルをクロスフェードするかどうかを設定します。 NO
mustSlideToChange BOOL このプロパティにYESを設定すると、スライド操作以外受け付けなくなります。タップでむやみに変更させたくない場合は便利ですね。 NO
minimumOverlapToChange CGFloat このプロパティは、mustSlideToChangeがYESのときに有効で、スライド操作の厳密性を定義できます。この値が1に近いほど、SVSegmentedThumbを選択したい箇所にぴったり合わせないと選択状態になりません。 0.66
touchTargetMargins UIEdgeInsets タッチの判定領域を縮小・拡大します。 UIEdgeInsetsMake(0, 0, 0, 0)
tintColor UIColor 背景のティントカラーを変更します。 [UIColor grayColor]
backgroundImage UIImage 背景に表示する画像を指定します。
height CGFloat 全体の高さを指定します。 32.0
thumbEdgeInset UIEdgeInsets SVSegmentedThumbのマージンを指定します。 UIEdgeInsetsMake(2, 2, 3, 2)
titleEdgeInsets UIEdgeInsets セクションのパディングを指定します。 UIEdgeInsetsMake(0, 10, 0, 10)
cornerRadius CGFloat 角丸の半径を指定します。 4.0
font UIFont フォントを指定します。 [UIFont boldSystemFontOfSize:15]
textColor UIColor テキストの色を指定します。 [UIColor grayColor]
textShadowColor UIColor テキストの影の色を指定します。 [UIColor blackColor]
textShadowOffset CGSize テキストの影のオフセットを指定します。 CGSizeMake(0, -1)

次に選択状態を表すSVSegmentedThumbのプロパティを見てみましょう。

SVSegmentedThumbのプロパティ
プロパティ 説明 デフォルト値
backgroundImage UIImage SVSegmentedThumbの背景を指定します。
highlightedBackgroundImage UIImage SVSegmentedThumbのハイライト時の背景を指定します。
tintColor UIColor ティントカラーを指定します。 [UIColor grayColor]
textColor UIColor テキストの色を指定します。 [UIColor whiteColor]
textShadowColor UIColor テキストの影の色を指定します。 [UIColor blackColor]
textShadowOffset CGSize テキストの影のオフセットを指定します。 CGSizeMake(0, -1)
shouldCastShadow BOOL SVSegmentedThumbのドロップシャドウの有無を指定します。 YES

イベントハンドラの指定方法

SVSegmentedControlでは次の2つの方法でイベントハンドラを指定できます。

Blocksによる指定

Blocksでイベントハンドラを指定する場合は、changeHandlerプロパティに以下のように定義するだけです。

segmentedControl.changeHandler = ^(NSUInteger newIndex) {
    // respond to index change
};

従来の方法での指定

おなじみの- addTarget:action:forControlEvents:メソッドで指定することもできます。

// Some method.
{
	[mySegmentedControl addTarget:self action:@selector(segmentedControlChangedValue:) forControlEvents:UIControlEventValueChanged];
}

- (void)segmentedControlChangedValue:(SVSegmentedControl*)segmentedControl {
	NSLog(@"segmentedControl did select index %i", segmentedControl.selectedIndex);
}

UISwitch、UISegmentedControlと異なるのは、- addTarget:action:forControlEvents:メソッドでイベントハンドラを指定した場合、選択中の値をもう一度選択したときもハンドリングしてくれることです。(これが割と便利!)

まとめ

UISwitchやUISegmentedControlを使ってて不便だなぁと感じていた方は是非一度試してみてください!