Control Fun(3)~SwitchとSegmented Control
2010. 08. 13
続いてセグメンテッドコントロールとスイッチを設置します。
セグメンテッドコントロールには2つのボタンがあり、その下に配置するスイッチを2個配したビューの表示/非表示を切り替えます。
ビュー上の2つのスイッチは連動しており、片方を操作するともう一方も追従します。
●サンプルコードと旧版本との違い
Apress社のサイト内にサンプルコードの『Beginning iPhone Development Projects Oct 12 2009/04 Control Fun』は、手元にある旧版『はじめてのiPhoneプログラミング』とレイアウトが異なります。
サンプルコードでは、セグメンテッドコントロールのボタンが、スイッチを配したビューの表示/非表示ではなく、スイッチと(後述するアクションシートを呼び出す)ボタンとの表示/非表示の切り替えになっています。
ここでは旧版本の内容を説明し、サンプルコードの内容は後日別途解説します。
●セグメンテッドコントロールとスイッチの設置
セグメンテッドコントロールやスイッチを設置するにあたり、本書のP.89の図にはP.63にあるようなアクションシートを呼び出すボタンが考慮されていません。
したがって手順を参考にレイアウトはそれっぽく決めます。
(頼りのサンプルコードは前述の通り異なりますので)
まずLibraryウィンドウからSegmented Controlをドラッグ&ドロップでViewウィンドウに置き、X:20、Y:244、W:280、H:44とします。
Viewウィンドウのテキスト部分をダブルクリックし、左側のSegment 0を『First』から『Show』に、右側のSegment 1を『Second』から『Hidden』に変更します。
気になる方は、InspectorウィンドウのAttributesタブで、『View』の『Drawing』でデフォルトでチェックされているClear Context Before DrawingとAutoresize Subviewsのチェックを外してください。
次にセグメンテッドコントロールで表示/非表示を切り替えるビューを設置します。
このビューの上にサブビューとしてスイッチ(とラベル)を配することで、複数のオブジェクトを一度に表示制御できます。
LibraryウィンドウからViewをドラッグ&ドロップでViewウィンドウに置き、X:20、Y:295、W:280、H:100とします。
(間違ってImage Viewを置くと、その上にスイッチが置けなくて悩むので注意してください)
View領域を明示するため、InspectorウィンドウのAttributesタブで、『View』の『Background』を『Light Gray Color』にします。
(逆に『Clear Color』に設定すれば、View領域を意識させなくできます)
気になる方は、『Drawing』でデフォルトでチェックされているClear Context Before DrawingとAutoresize Subviewsのチェックを外してください。
『Opaque』のチェックは入れたままにします。
(『Opaque』のチェックが入っていても、『Background』で『Clear Color』を選択した場合には、ちゃんと透明化されます)
『Interaction』の『User Interaction Enabled』はチェックを入れたままにします。
(Viewそのものは操作しませんが、サブビューとしてスイッチを配する際に継承されるため)
次にViewの上にLabelとSwitchを2個ずつドラッグ&ドロップします。
各パーツの位置とサイズは下表の通りです。
ラベルの属性はInspectorウィンドウのAttributesタブで、『Label』の『Text』をそれぞれ『Left』と『Right』に、『Layout』の『Alignment』を中央揃えにします。
気になる方は、『View』の『Drawing』でデフォルトでチェックされているClear Context Before Drawing、Clip Subviews、Autoresize Subviewsのチェックを外してください。
スイッチの属性は特に設定する項目は無く、強いて言えば識別を容易にする為に、DocumentウィンドウのNameを修正するくらいです。
気になる方は、『View』の『Drawing』でデフォルトでチェックされているClear Context Before DrawingとAutoresize Subviewsのチェックを外してください。
『Interaction』の『User Interaction Enabled』はチェックを入れたままにします。
Documentウィンドウでは下図のような構成になります。
●アウトレットとプロパティ、アクションの宣言
パーツのレイアウトが済んだので、Interface BuilderからXcodeに戻りコードの記述を行います。
まずControl_FunViewController.hを開いて、アウトレットとプロパティ、アクションを宣言します。
(太字が追加した部分)
最初に#defineで、セグメンテッドコントロールの定数kShowSegmentIndexを設定しています。
この定数はビューの表示/非表示を行うアクションメソッドtoggleShowHide:で使用します。
セグメンテッドコントロールは前回のスライダと同様に、アクションメソッドで選択値を取得するのでアウトレットは不要になりますので、必要なアウトレットはスイッチ2つと表示/非表示を行うビュー用の3つになります。
プロパティはアウトレットと同名のものが3つになります。
アクションは、連動して動作するスイッチ用のswitchChanged:と、ビューの表示/非表示を切り替えるセグメンテッドコントロール用のtoggleShowHide:になります。
●プロパティとアクションの実装
Control_FunViewController.mを開き、宣言したプロパティとアクションを実装します。
(太字が追加した部分)
switchChanged:メソッドは、ビュー上にある2つのスイッチの内、一方を操作するともう一方も連動させるメソッドです。
前回のスライダ同様、引数のsenderをUISwitchでキャストし、onプロパティでスイッチがONかOFFかを取得します。
そして、取得したプロパティ値で両スイッチを設定し直しています。
普通に考えれば条件分岐を使いそうなところですが、今回のような場合ですと条件分岐で個別に設定するより両方に設定した方が、コードの記述量も減りますし処理が速くなるとのことです。
(『こういうやり方もある』というtipsは、初心者には地味に嬉しいですね)
toggleShowHide:メソッドは、セグメンテッドコントロールの値を取得してビューの表示/非表示を切り替えるメソッドです。
引数のsenderをUISegmentedControlでキャストし、selectedSegmentIndexプロパティで選択されたボタンの値を取得します。
その値をkShowSegmentIndex定数で比較し、hiddenプロパティでビューの表示/非表示を設定します。
setHidden:メソッドはアクセサメソッドの命名規則で、BOOL値のプロパティを表しています。
この場合UIViewのhiddenプロパティで、ゲッタがisHidden、セッタがsetHiddenとなります。
(詳細は『Cocoa Core Competencies:Accessor method』を参照してください)
・on
(UISwitchクラス)
@property(nonatomic, getter=isOn) BOOL on
スイッチのON/OFF状態を決定するブール値です。
このプロパティは、UISwitchオブジェクトがONまたはOFFかどうかを、取得または(アニメーションを除く)値の設定を行います。
・setOn:animated:
(UISwitchクラス)
- (void)setOn:(BOOL)on animated:(BOOL)animated
スイッチをONまたはOFFの状態に設定し、オプションで遷移アニメーションを指定できます。
(※上手く訳せなかったので原文を掲載します)
Setting the switch to either position does not result in an action message being sent.
on:YESの場合、スイッチはONの位置に、NOの場合OFFの位置に反転します。
スイッチが既に指定された位置の場合は何も起こりません。
animated:YESではスイッチの反転アニメーションを行い、NOではアニメーションしません。
・hidden
(UIViewクラス)
@property(nonatomic, getter=isHidden) BOOL hidden
レシーバを非表示にするかを決定するブール値です。
YESの場合はレシーバが非表示に、そうでない場合はNOになります。
デフォルト値はNOです。
ビューを非表示にするとウィンドウからは見えなくなり、入力イベントに応答できなくなります。
しかしスーパービューのサブビューリストには残っており、自動リサイズにも対応します。
ビューを非表示にすると、それらが持つサブビューとすべてのビューの子孫を隠す効果があります。
この効果は暗黙的で、レシーバの子孫の非表示状態を変更しません。
ウィンドウの現在のファーストレスポンダのビューが非表示になると、次に有効なキービューが新しいファーストレスポンダになります。
このプロパティの値はレシーバの状態のみに反映され、ビュー階層内のレシーバの先祖の状態には反映されません。
したがって、先祖が非表示になっているために非表示になっているレシーバは、このプロパティをNOにすることができます。
●アウトレットとアクションの接続
Interface Builderに戻り、DocumentウィンドウでFile's Ownerを選択し、InspectorウィンドウのConnectionsタブを開きます。
アウトレットとアクションを以下のように接続します。
・Outlets
leftSwitch → 左スイッチ
rightSwitch → 右スイッチ
switchView → スイッチの親ビュー
・Received Actions
switchChanged: → 両スイッチ
toggleShowHide: → セグメンテッドコントロール
接続が完了しましたら、保存してビルドと実行を行い動作を確認してください。
●UISwitchクラス
UISwitchクラスは、機内モードなどのサービス設定などで使用されるON/OFFボタンの生成と管理に使用します。
これらのオブジェクトはスイッチとして知られています。
UISwitchクラスは、ON/OFF状態を制御するプロパティとメソッドを宣言します。
UISliderのように、ユーザがスイッチ制御を操作(反転)するとUIControlEventValueChangedイベントが生成され、その制御の結果が(適切に構成されていれば)アクションメッセージとして送信されます。
UISwitchクラスはカスタマイズできません。
参考文献
・UISwitch Class Reference
・UISegmentedControl Class Reference
・UIView Class Reference
・Cocoa Core Competencies:Accessor method
セグメンテッドコントロールには2つのボタンがあり、その下に配置するスイッチを2個配したビューの表示/非表示を切り替えます。
ビュー上の2つのスイッチは連動しており、片方を操作するともう一方も追従します。
●サンプルコードと旧版本との違い
Apress社のサイト内にサンプルコードの『Beginning iPhone Development Projects Oct 12 2009/04 Control Fun』は、手元にある旧版『はじめてのiPhoneプログラミング』とレイアウトが異なります。
サンプルコードでは、セグメンテッドコントロールのボタンが、スイッチを配したビューの表示/非表示ではなく、スイッチと(後述するアクションシートを呼び出す)ボタンとの表示/非表示の切り替えになっています。
ここでは旧版本の内容を説明し、サンプルコードの内容は後日別途解説します。
●セグメンテッドコントロールとスイッチの設置
セグメンテッドコントロールやスイッチを設置するにあたり、本書のP.89の図にはP.63にあるようなアクションシートを呼び出すボタンが考慮されていません。
したがって手順を参考にレイアウトはそれっぽく決めます。
(頼りのサンプルコードは前述の通り異なりますので)
まずLibraryウィンドウからSegmented Controlをドラッグ&ドロップでViewウィンドウに置き、X:20、Y:244、W:280、H:44とします。
Viewウィンドウのテキスト部分をダブルクリックし、左側のSegment 0を『First』から『Show』に、右側のSegment 1を『Second』から『Hidden』に変更します。
気になる方は、InspectorウィンドウのAttributesタブで、『View』の『Drawing』でデフォルトでチェックされているClear Context Before DrawingとAutoresize Subviewsのチェックを外してください。
次にセグメンテッドコントロールで表示/非表示を切り替えるビューを設置します。
このビューの上にサブビューとしてスイッチ(とラベル)を配することで、複数のオブジェクトを一度に表示制御できます。
LibraryウィンドウからViewをドラッグ&ドロップでViewウィンドウに置き、X:20、Y:295、W:280、H:100とします。
(間違ってImage Viewを置くと、その上にスイッチが置けなくて悩むので注意してください)
View領域を明示するため、InspectorウィンドウのAttributesタブで、『View』の『Background』を『Light Gray Color』にします。
(逆に『Clear Color』に設定すれば、View領域を意識させなくできます)
気になる方は、『Drawing』でデフォルトでチェックされているClear Context Before DrawingとAutoresize Subviewsのチェックを外してください。
『Opaque』のチェックは入れたままにします。
(『Opaque』のチェックが入っていても、『Background』で『Clear Color』を選択した場合には、ちゃんと透明化されます)
『Interaction』の『User Interaction Enabled』はチェックを入れたままにします。
(Viewそのものは操作しませんが、サブビューとしてスイッチを配する際に継承されるため)
次にViewの上にLabelとSwitchを2個ずつドラッグ&ドロップします。
各パーツの位置とサイズは下表の通りです。
X | Y | W | H | |
Label (Left) | 46 | 24 | 42 | 21 |
Label (Right) | 192 | 24 | 42 | 21 |
Switch (Left) | 20 | 53 | 94 | 27 |
Switch (Right) | 166 | 53 | 94 | 27 |
ラベルの属性はInspectorウィンドウのAttributesタブで、『Label』の『Text』をそれぞれ『Left』と『Right』に、『Layout』の『Alignment』を中央揃えにします。
気になる方は、『View』の『Drawing』でデフォルトでチェックされているClear Context Before Drawing、Clip Subviews、Autoresize Subviewsのチェックを外してください。
スイッチの属性は特に設定する項目は無く、強いて言えば識別を容易にする為に、DocumentウィンドウのNameを修正するくらいです。
気になる方は、『View』の『Drawing』でデフォルトでチェックされているClear Context Before DrawingとAutoresize Subviewsのチェックを外してください。
『Interaction』の『User Interaction Enabled』はチェックを入れたままにします。
Documentウィンドウでは下図のような構成になります。
●アウトレットとプロパティ、アクションの宣言
パーツのレイアウトが済んだので、Interface BuilderからXcodeに戻りコードの記述を行います。
まずControl_FunViewController.hを開いて、アウトレットとプロパティ、アクションを宣言します。
(太字が追加した部分)
#import <UIKit/UIKit.h>
#define kShowSegmentIndex 0
@interface Control_FunViewController : UIViewController {
IBOutlet UITextField *nameField;
IBOutlet UITextField *numberField;
IBOutlet UILabel *sliderLabel;
IBOutlet UISwitch *leftSwitch;
IBOutlet UISwitch *rightSwitch;
IBOutlet UIView *switchView;
}
@property (nonatomic, retain) UITextField *nameField;
@property (nonatomic, retain) UITextField *numberField;
@property (nonatomic, retain) UILabel *sliderLabel;
@property (nonatomic, retain) UISwitch *leftSwitch;
@property (nonatomic, retain) UISwitch *rightSwitch;
@property (nonatomic, retain) UIView *switchView;
- (IBAction)textFieldDoneEditing:(id)sender;
- (IBAction)backgroundClick:(id)sender;
- (IBAction)sliderChanged:(id)sender;
- (IBAction)switchChanged:(id)sender;
- (IBAction)toggleShowHide:(id)sender;
@end
#define kShowSegmentIndex 0
@interface Control_FunViewController : UIViewController {
IBOutlet UITextField *nameField;
IBOutlet UITextField *numberField;
IBOutlet UILabel *sliderLabel;
IBOutlet UISwitch *leftSwitch;
IBOutlet UISwitch *rightSwitch;
IBOutlet UIView *switchView;
}
@property (nonatomic, retain) UITextField *nameField;
@property (nonatomic, retain) UITextField *numberField;
@property (nonatomic, retain) UILabel *sliderLabel;
@property (nonatomic, retain) UISwitch *leftSwitch;
@property (nonatomic, retain) UISwitch *rightSwitch;
@property (nonatomic, retain) UIView *switchView;
- (IBAction)textFieldDoneEditing:(id)sender;
- (IBAction)backgroundClick:(id)sender;
- (IBAction)sliderChanged:(id)sender;
- (IBAction)switchChanged:(id)sender;
- (IBAction)toggleShowHide:(id)sender;
@end
最初に#defineで、セグメンテッドコントロールの定数kShowSegmentIndexを設定しています。
この定数はビューの表示/非表示を行うアクションメソッドtoggleShowHide:で使用します。
セグメンテッドコントロールは前回のスライダと同様に、アクションメソッドで選択値を取得するのでアウトレットは不要になりますので、必要なアウトレットはスイッチ2つと表示/非表示を行うビュー用の3つになります。
プロパティはアウトレットと同名のものが3つになります。
アクションは、連動して動作するスイッチ用のswitchChanged:と、ビューの表示/非表示を切り替えるセグメンテッドコントロール用のtoggleShowHide:になります。
●プロパティとアクションの実装
Control_FunViewController.mを開き、宣言したプロパティとアクションを実装します。
(太字が追加した部分)
#import "Control_FunViewController.h"
@implementation Control_FunViewController
@synthesize nameField;
@synthesize numberField;
@synthesize sliderLabel;
@synthesize leftSwitch;
@synthesize rightSwitch;
@synthesize switchView;
- (IBAction)switchChanged:(id)sender {
UISwitch *whichSwitch = (UISwitch *)sender;
BOOL setting = whichSwitch.isOn;
[leftSwitch setOn:setting animated:YES];
[rightSwitch setOn:setting animated:YES];
}
- (IBAction)toggleShowHide:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
NSInteger segment = segmentedControl.selectedSegmentIndex;
if (segment == kShowSegmentIndex) [switchView setHidden:NO];
else [switchView setHidden:YES];
}
...
@implementation Control_FunViewController
@synthesize nameField;
@synthesize numberField;
@synthesize sliderLabel;
@synthesize leftSwitch;
@synthesize rightSwitch;
@synthesize switchView;
- (IBAction)switchChanged:(id)sender {
UISwitch *whichSwitch = (UISwitch *)sender;
BOOL setting = whichSwitch.isOn;
[leftSwitch setOn:setting animated:YES];
[rightSwitch setOn:setting animated:YES];
}
- (IBAction)toggleShowHide:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;
NSInteger segment = segmentedControl.selectedSegmentIndex;
if (segment == kShowSegmentIndex) [switchView setHidden:NO];
else [switchView setHidden:YES];
}
...
switchChanged:メソッドは、ビュー上にある2つのスイッチの内、一方を操作するともう一方も連動させるメソッドです。
前回のスライダ同様、引数のsenderをUISwitchでキャストし、onプロパティでスイッチがONかOFFかを取得します。
そして、取得したプロパティ値で両スイッチを設定し直しています。
普通に考えれば条件分岐を使いそうなところですが、今回のような場合ですと条件分岐で個別に設定するより両方に設定した方が、コードの記述量も減りますし処理が速くなるとのことです。
(『こういうやり方もある』というtipsは、初心者には地味に嬉しいですね)
toggleShowHide:メソッドは、セグメンテッドコントロールの値を取得してビューの表示/非表示を切り替えるメソッドです。
引数のsenderをUISegmentedControlでキャストし、selectedSegmentIndexプロパティで選択されたボタンの値を取得します。
その値をkShowSegmentIndex定数で比較し、hiddenプロパティでビューの表示/非表示を設定します。
setHidden:メソッドはアクセサメソッドの命名規則で、BOOL値のプロパティを表しています。
この場合UIViewのhiddenプロパティで、ゲッタがisHidden、セッタがsetHiddenとなります。
(詳細は『Cocoa Core Competencies:Accessor method』を参照してください)
・on
(UISwitchクラス)
@property(nonatomic, getter=isOn) BOOL on
スイッチのON/OFF状態を決定するブール値です。
このプロパティは、UISwitchオブジェクトがONまたはOFFかどうかを、取得または(アニメーションを除く)値の設定を行います。
・setOn:animated:
(UISwitchクラス)
- (void)setOn:(BOOL)on animated:(BOOL)animated
スイッチをONまたはOFFの状態に設定し、オプションで遷移アニメーションを指定できます。
(※上手く訳せなかったので原文を掲載します)
Setting the switch to either position does not result in an action message being sent.
on:YESの場合、スイッチはONの位置に、NOの場合OFFの位置に反転します。
スイッチが既に指定された位置の場合は何も起こりません。
animated:YESではスイッチの反転アニメーションを行い、NOではアニメーションしません。
・hidden
(UIViewクラス)
@property(nonatomic, getter=isHidden) BOOL hidden
レシーバを非表示にするかを決定するブール値です。
YESの場合はレシーバが非表示に、そうでない場合はNOになります。
デフォルト値はNOです。
ビューを非表示にするとウィンドウからは見えなくなり、入力イベントに応答できなくなります。
しかしスーパービューのサブビューリストには残っており、自動リサイズにも対応します。
ビューを非表示にすると、それらが持つサブビューとすべてのビューの子孫を隠す効果があります。
この効果は暗黙的で、レシーバの子孫の非表示状態を変更しません。
ウィンドウの現在のファーストレスポンダのビューが非表示になると、次に有効なキービューが新しいファーストレスポンダになります。
このプロパティの値はレシーバの状態のみに反映され、ビュー階層内のレシーバの先祖の状態には反映されません。
したがって、先祖が非表示になっているために非表示になっているレシーバは、このプロパティをNOにすることができます。
●アウトレットとアクションの接続
Interface Builderに戻り、DocumentウィンドウでFile's Ownerを選択し、InspectorウィンドウのConnectionsタブを開きます。
アウトレットとアクションを以下のように接続します。
・Outlets
leftSwitch → 左スイッチ
rightSwitch → 右スイッチ
switchView → スイッチの親ビュー
・Received Actions
switchChanged: → 両スイッチ
toggleShowHide: → セグメンテッドコントロール
接続が完了しましたら、保存してビルドと実行を行い動作を確認してください。
●UISwitchクラス
UISwitchクラスは、機内モードなどのサービス設定などで使用されるON/OFFボタンの生成と管理に使用します。
これらのオブジェクトはスイッチとして知られています。
UISwitchクラスは、ON/OFF状態を制御するプロパティとメソッドを宣言します。
UISliderのように、ユーザがスイッチ制御を操作(反転)するとUIControlEventValueChangedイベントが生成され、その制御の結果が(適切に構成されていれば)アクションメッセージとして送信されます。
UISwitchクラスはカスタマイズできません。
参考文献
・UISwitch Class Reference
・UISegmentedControl Class Reference
・UIView Class Reference
・Cocoa Core Competencies:Accessor method
はじめてのiPhone3プログラミング (2009/12/17) Dave Mark、Jeff LaMarche 他 商品詳細を見る |