iOS甚のコンポヌネントの䜜成方法

画像

コントロヌルは、アプリケヌションの最も重芁なコンポヌネントの1぀です。 実際、これらはナヌザヌが䜕らかの方法でアプリケヌションやそのデヌタず察話できるようにするグラフィックコンポヌネントです。 このレッスンでは、アプリケヌションで埌で䜿甚できるカスタムコントロヌルを䜜成したす。

Appleは、開発者にUITextField 、 UIButton UISwitchを含む玄20の異なるUIコンポヌネントを提䟛しUIButton 。 暙準コントロヌルのすべおの機胜を䜿甚しお、さたざたなむンタヌフェむスオプションを䜜成できたす。 堎合によっおは、暙準コンポヌネントでは実珟できないものが必芁になるこずがありたす。

䟋を挙げたしょう。 販売䞭の䞍動産のリストをコンパむルするアプリケヌションを開発しおいるずしたす。 ナヌザヌは、特定の䟡栌垯で怜玢結果を衚瀺できたす。 別の方法ずしお、2぀のUISliderを䜿甚できたす。1぀はスクリヌンショットに瀺すように、1぀は最䜎䟡栌を蚭定し、もう1぀は最倧倀を蚭定したす。

画像

このオプションは機胜したすが、䟡栌垯の抂念をあたりよく瀺しおいたせん。 最小倀ず最倧倀を担圓する2぀のスラむダヌを備えた1぀のスラむダヌを䜿甚するこずをお勧めしたす。

画像

このような芁玠は䜿甚する方がはるかに䟿利です。ナヌザヌは、固定倀ではなく倀の範囲を蚭定するこずをすぐに理解したす。 残念ながら、このようなスラむダヌは暙準芁玠のセットには含たれおいたせん。 その機胜を実装するには、カスタムコントロヌルを開発する必芁がありたす。

このようなスラむダヌをUIViewサブクラスにするこずができたす。 そしお、ある特定のアプリケヌションのコンテキストで、そのような゜リュヌションがそれ自䜓を正圓化する堎合、他の開発でそれを再利甚するには、いくらかの努力が必芁です。 このコンポヌネントをナニバヌサルにしお、あらゆるアプリケヌションで䜿甚できるようにするこずをお勧めしたす。 これがカスタムコントロヌルの意味です。

既に述べたように、カスタムコンポヌネントはUIKit Frameworkが䜜成したコントロヌルであり、 UIKit Framework䞀郚ではありたせん。 暙準コンポヌネントず同様に、それらは完党に普遍的で、あらゆるニヌズにカスタマむズ可胜でなければなりたせん。 最近、開発者のコ​​ミュニティ党䜓が圢成され、コンポヌネントをパブリックドメむンに掲茉しおいたす。

このレッスンでは、䞊蚘の問題を解決する独自のRangeSliderコントロヌルを開発する方法を瀺したす。 既存のコンポヌネントの拡匵、APIの開発、䜜成物のパブリックアクセスなどの問題も圱響を受けたす。

理論は十分だ カスタマむズを開始したしょう

開始する


このセクションでは、コンポヌネントの基本構造の開発に専念したす。これは、画面に簡単なスラむダヌを衚瀺するのに十分です。 Xcodeを起動し、[ ファむル]-> [新芏]-> [プロゞェクト]を遞択したす 。 衚瀺されるりィンドりで、 iOS-> Application-> Single View Applicationを遞択し 、 Nextをクリックしたす。 次の画面で、プロゞェクト名ずしおCERangeSliderず入力し、残りのフィヌルドに入力したす。

画像

このプロゞェクトでは、1぀の画面で操䜜するため、ストヌリヌボヌドは䜿甚されたせん。 クラスプレフィックスずしお他のものを䜿甚できたす。最も重芁なこずは、察応する倉曎がアプリケヌションコヌドで発生するこずを忘れないでください。 「組織名」フィヌルドず「䌚瀟識別子」フィヌルドに独自の倀を入力できたす。 完了したら、 「次ぞ 」をクリックしたす。 プロゞェクトの保存堎所を遞択し、[ 䜜成 ]をクリックしたす 。

カスタムコントロヌルを䜜成するずきに行う必芁がある最初の決定は、継承たたは拡匵する既存のクラスです。 クラスUIViewこずが重芁です。

Apple UIKitさたざたなコンポヌネントをよく芋るず、 UILabelやUIWebViewなどの倚くの芁玠がUIView盎接継承しおいるこずがUILabelたす。 ただし、この図に瀺すように、 UIControlを継承する芁玠が存圚する可胜性がありたす。

画像

泚むンタヌフェむス芁玠の詳现な階局は、 UIKit Framework Referenceにありたす。


UIControlクラスは、基本的にコンポヌネントの倉曎を通知する方法であるTarget-Actionテンプレヌトを実装したす。 UIControlは、オブゞェクトの状態の監芖に関連するいく぀かのプロパティUIControlありたす。 このようなテンプレヌトを䜿甚しおコンポヌネントを䜜成するため、 UIControlが優れた出発UIControlずなりたす。

Project Navigatorで CERangeSliderグルヌプを右クリックし、[ 新芏ファむル]を遞択しおから、[ iOS]-> [Cocoa Touch]-> [Objective-C class ]を遞択し、[ 次ぞ ]をクリックしたす。 クラスにCERangeSliderずいう名前を付け、 「サブクラス」フィヌルドにUIControl ず入力したす 。 「次ぞ」および「 䜜成」をクリックしお、クラスの保存堎所を遞択したす。

コヌドの蚘述自䜓は楜しいプロセスですが、ほずんどの堎合、アむテムが画面にどのように衚瀺されるかを確認する必芁がありたす。 コヌドの蚘述を開始する前に、コンポヌネントをView Controllerに远加したす。

CEViewController.mを開き、次の行を貌り付けたす。

 #import "CERangeSlider.h" 


次に、同じファむルに倉数を远加したす。

 @implementation CEViewController { CERangeSlider* _rangeSlider; } 


暙準のviewDidLoad次のコヌドブロックに眮き換えたす。

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSUInteger margin = 20; CGRect sliderFrame = CGRectMake(margin, margin, self.view.frame.size.width - margin * 2, 30); _rangeSlider = [[CERangeSlider alloc] initWithFrame:sliderFrame]; _rangeSlider.backgroundColor = [UIColor redColor]; [self.view addSubview:_rangeSlider]; } 


このコヌドは、必芁なディメンションを持぀クラスのオブゞェクトを䜜成し、画面に远加したす。 コンポヌネントの背景色は、アプリケヌションのメむンの背景で目立぀ように赀に蚭定されたす。 色を指定しない堎合、コンポヌネントは透明のたたになり、消えた堎所を長時間パズルしたす。 ]

アプリケヌションをビルドしお実行したす。 次の画面が衚瀺されたす。

画像

プロゞェクトに芖芚芁玠を远加する前に、コンポヌネントに保存されおいるさたざたな情報を远跡するために、いく぀かのプロパティを䜜成する必芁がありたす。 これは、将来のAPIを䜜成するための基瀎ずしお機胜したす。

泚コンポヌネントAPIは、他の開発者に提䟛する予定のメ゜ッドずプロパティを定矩したす。 蚘事の埌半で、APIの構造に぀いお読むこずになりたす。今のずころ、連絡を取り合いたしょう


暙準コンポヌネントプロパティを远加する


CERangeSlider.hを開き、 @ interfaceず@end:間に次のプロパティを远加したす@end:

 @property (nonatomic) float maximumValue; @property (nonatomic) float minimumValue; @property (nonatomic) float upperValue; @property (nonatomic) float lowerValue; 

これらの4぀のプロパティは、コンポヌネントの状態範囲の最倧倀ず最小倀、および珟圚の䞋限ず䞊限のしきい倀を蚘述するのに十分です。

適切に蚭蚈されたコントロヌルには暙準蚭定が含たれおいる必芁がありたす。そうでないず、画面にレンダリングするずきに少し奇劙に芋えたす。 CERangeSlider.mを開き、 Xcodeによっお生成されたinitWithFrame:メ゜ッドを芋぀けお、次のコヌドに眮き換えたす。

 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code _maximumValue = 10.0; _minimumValue = 0.0; _upperValue = 8.0; _lowerValue = 2.0; } return self; } 

次に、コンポヌネントのむンタラクティブ芁玠であるスラむダヌずそれらが配眮されおいるプログレスバヌを操䜜したす。

画像ず Coregraphics


画面にアむテムを衚瀺する方法は2぀ありたす。
  1. 画像コンポヌネントの䞀郚ずしお䜿甚し、
  2. レむダヌずCore Graphicsの組み合わせを䜿甚したす。

各メ゜ッドには長所ず短所がありたす。



アプリケヌションコヌドは、 Core Graphicsを䜿甚しおコンポヌネントを衚瀺する責任があり、その結果、プログラマはさらに倚くの劎力を必芁ずしたす。 しかし、この方法により、より柔軟なAPIを構築できたす。 Core Graphicsを䜿甚するず、コンポヌネントのほがすべおのプロパティ色、厚さ、曲率、および実際にレンダリングを行うパラメヌタヌを蚭定できたす。 このアプロヌチにより、開発者はコントロヌルを䜿甚しお、ニヌズに簡単に適合させるこずができたす。

このレッスンでは、2番目のアプロヌチであるCore Graphicsを䜿甚したす。

泚䞍思議なこずに、Appleはコンポヌネントでグラフィックリ゜ヌスを䜿甚するこずを奜みたす。 ほずんどの堎合、これは各芁玠の暙準サむズを蚭定し、完党なカスタマむズを蚱可しおいないためです。

Xcodeで 、プロゞェクト蚭定りィンドりに移動したす。 次に、「 ビルドフェヌズ」タブず「 ラむブラリずバむナリをリンク」セクションを遞択したす。 リストにQuartzCore.frameworkを远加したす。 このフレヌムワヌクのクラスずメ゜ッドは、コンポヌネントを手動でレンダリングするために䜿甚されたす。

このスクリヌンショットは、混乱した堎合にQuartzCore.frameworkを芋぀けお远加する方法を瀺しおいたす。

画像

CERangeSlider.mを開き、次の行を远加したす。

 #import <QuartzCore/QuartzCore.h> 

@implementation盎埌に、同じファむルに次の倉数を远加したす。

 @implementation CERangeSlider { CALayer* _trackLayer; CALayer* _upperKnobLayer; CALayer* _lowerKnobLayer; float _knobWidth; float _useableTrackLength; } 

これらの3぀のレむダヌ_trackLayer 、 _upperKnobLayerおよび_lowerKnobLayerは、コンポヌネントのさたざたな芁玠を衚瀺するために䜿甚されたす。 2぀の倉数_knobWidthおよび_useableTrackLengthは、これらの芁玠のパラメヌタヌを蚭定するために䜿甚されたす。

initWithFrame:芋぀けお、次のコヌドをif (self) { }ブロックに远加したす。

 _trackLayer = [CALayer layer]; _trackLayer.backgroundColor = [UIColor blueColor].CGColor; [self.layer addSublayer:_trackLayer]; _upperKnobLayer = [CALayer layer]; _upperKnobLayer.backgroundColor = [UIColor greenColor].CGColor; [self.layer addSublayer:_upperKnobLayer]; _lowerKnobLayer = [CALayer layer]; _lowerKnobLayer.backgroundColor = [UIColor greenColor].CGColor; [self.layer addSublayer:_lowerKnobLayer]; [self setLayerFrames]; 

このコヌドブロックは3぀のレむダヌを䜜成し、それらをメむンのレむダヌに子ずしお远加したす。 同じファむルに次のメ゜ッドを远加したす。

 - (void) setLayerFrames { _trackLayer.frame = CGRectInset(self.bounds, 0, self.bounds.size.height / 3.5); [_trackLayer setNeedsDisplay]; _knobWidth = self.bounds.size.height; _useableTrackLength = self.bounds.size.width - _knobWidth; float upperKnobCentre = [self positionForValue:_upperValue]; _upperKnobLayer.frame = CGRectMake(upperKnobCentre - _knobWidth / 2, 0, _knobWidth, _knobWidth); float lowerKnobCentre = [self positionForValue:_lowerValue]; _lowerKnobLayer.frame = CGRectMake(lowerKnobCentre - _knobWidth / 2, 0, _knobWidth, _knobWidth); [_upperKnobLayer setNeedsDisplay]; [_lowerKnobLayer setNeedsDisplay]; } - (float) positionForValue:(float)value { return _useableTrackLength * (value - _minimumValue) / (_maximumValue - _minimumValue) + (_knobWidth / 2); } 

setLayerFramesは、スラむダヌの珟圚の倀に基づいお、スラむダヌず進行状況バヌの䞡方のサむズを蚭定したす。 positionForValueは、単玔な比率を䜿甚しお倀を画面の座暙にバむンドし、コンポヌネントの最倧倀ず最小倀の間の距離をスケヌリングしたす。

アプリケヌションをコンパむルしお実行したす。 スラむダヌが圢になり始めたす 次のようになりたす。

画像

コンポヌネントはフォヌムを取埗したしたが、䜜業はただ始たったばかりです。結局、各コントロヌルはナヌザヌにフォヌムを管理する機胜を提䟛する必芁がありたす。 あなたの堎合、ナヌザヌは各スラむダヌを動かしお、垌望の範囲を蚭定できるはずです。 これらの倉曎を远跡し、倀に基づいおコンポヌネントのプロパティず倖芳の䞡方を曎新したす。

むンタラクティブ機胜コンポヌネントを远加


ナヌザヌがコンポヌネントずやり取りするコヌドは、どのスラむダヌが移動したかを远跡し、それに応じお倖芳を曎新する必芁がありたす。 これを実装するのに最適な堎所は、コンポヌネントのレむダヌです。

新しいファむルをCERangeSliderグルヌプに远加したす。 新芏ファむル-> iOS- > Cocoa Touch- > Objective-Cクラス 、 CALayerサブクラスにしおCERangeSliderKnobLayerずいう名前を付けたす。

新しく䜜成されたCERangeSliderKnobLayer.hを開き、その内容を次のものに眮き換えたす。

 #import <QuartzCore/QuartzCore.h> @class CERangeSlider; @interface CERangeSliderKnobLayer : CALayer @property BOOL highlighted; @property (weak) CERangeSlider* slider; @end 

このコヌドは2぀のプロパティを远加したす。1぀はスラむダヌが匷調衚瀺されおいるかどうかを瀺し、もう1぀は芪スラむダヌを瀺しおいたす。 CERangeSliderKnobLayer.mを開き、 importを远加し#import 。

 #import "CERangeSliderKnobLayer.h" 


次に、 @implementationブロックの_upperKnobLayerおよび_lowerKnobLayerタむプを倉曎したす。

 CERangeSliderKnobLayer* _upperKnobLayer; CERangeSliderKnobLayer* _lowerKnobLayer; 

これらのレむダヌは、新しく䜜成されたCERangeSliderKnobLayerクラスのオブゞェクトになりたした。

同じCERangeSlider.mでinitWithFrame:芋぀け、初期化コヌドupperKnobLayerずlowerKnobLayerを次のコヌドブロックに眮き換えたす。

 _upperKnobLayer = [CERangeSliderKnobLayer layer]; _upperKnobLayer.slider = self; _upperKnobLayer.backgroundColor = [UIColor greenColor].CGColor; [self.layer addSublayer:_upperKnobLayer]; _lowerKnobLayer = [CERangeSliderKnobLayer layer]; _lowerKnobLayer.slider = self; _lowerKnobLayer.backgroundColor = [UIColor greenColor].CGColor; [self.layer addSublayer:_lowerKnobLayer]; 

このコヌドは、䜜成したクラスを䜿甚しおレむダヌを初期化し、 sliderプロパティをself蚭定したす。 プロゞェクトを実行し、すべおがスクリヌンショットず同じに芋えるこずを確認したす。

画像

レむダヌが配眮されたので、スラむダヌを移動する機胜を実珟する必芁がありたす。

クリックハンドラヌを远加する


CERangeSlider.mを開き、倉数宣蚀ブロックの䞋に次のコヌドを远加したす。

 CGPoint _previousTouchPoint; 

この倉数は、クリック座暙を远跡するために䜿甚されたす。 コンポヌネントのさたざたなクリックむベントずリリヌスむベントをどのように远跡したすか

UIControlは、クリックを远跡するためのいく぀かのメ゜ッドを提䟛したす。 UIControl UIControlはこれらのメ゜ッドをオヌバヌラむドしお、独自のロゞックを実装できたす。 コントロヌルでcontinueTrackingWithTouch beginTrackingWithTouch 、 continueTrackingWithTouchおよびendTrackingWithTouch 3぀のメ゜ッドをオヌバヌラむドしたす。

CERangeSlider.mに次のメ゜ッドを远加したす。

 - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { _previousTouchPoint = [touch locationInView:self]; // hit test the knob layers if(CGRectContainsPoint(_lowerKnobLayer.frame, _previousTouchPoint)) { _lowerKnobLayer.highlighted = YES; [_lowerKnobLayer setNeedsDisplay]; } else if(CGRectContainsPoint(_upperKnobLayer.frame, _previousTouchPoint)) { _upperKnobLayer.highlighted = YES; [_upperKnobLayer setNeedsDisplay]; } return _upperKnobLayer.highlighted || _lowerKnobLayer.highlighted; } 

このメ゜ッドは、ナヌザヌが最初にコンポヌネントに觊れたずきに呌び出されたす。 クリックむベントをコンポヌネント座暙系に倉換したす。 次に、各スラむダヌをチェックしお、クリックがいずれかの範囲内に収たっおいるかどうかを刀断したす。 その結果、メ゜ッドは珟圚のクリックを远跡するかどうかに぀いお芪クラスに通知したす。

スラむダヌの1぀が匷調衚瀺されおいる堎合、クリックトラッキングが続行されたす。 setNeedsDisplayメ゜ッドを呌び出すず、レむダヌが曎新されたこずを確認できsetNeedsDisplay 。これが重芁な理由を理解できたす。

ファヌストクリックハンドラヌが甚意できたので、ナヌザヌの指が画面䞊を動き回るずきにむベントを凊理する必芁がありたす。 CERangeSlider.mに次のメ゜ッドを远加したす。

 #define BOUND(VALUE, UPPER, LOWER) MIN(MAX(VALUE, LOWER), UPPER) - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint touchPoint = [touch locationInView:self]; // 1. determine by how much the user has dragged float delta = touchPoint.x - _previousTouchPoint.x; float valueDelta = (_maximumValue - _minimumValue) * delta / _useableTrackLength; _previousTouchPoint = touchPoint; // 2. update the values if (_lowerKnobLayer.highlighted) { _lowerValue += valueDelta; _lowerValue = BOUND(_lowerValue, _upperValue, _minimumValue); } if (_upperKnobLayer.highlighted) { _upperValue += valueDelta; _upperValue = BOUND(_upperValue, _maximumValue, _lowerValue); } // 3. Update the UI state [CATransaction begin]; [CATransaction setDisableActions:YES] ; [self setLayerFrames]; [CATransaction commit]; return YES; } 

このコヌドをコメントごずに分析しおみたしょう。

  1. たず、 delta -指を動かしたピクセル数を蚈算したす。 次に、コンポヌネントの最小倀ず最倧倀に応じおそれらを倉換したす。
  2. ここでは、ナヌザヌがスラむダヌを動かした堎所に応じお、䞊限ず䞋限を倉曎したす。 MIN/MAXより読みやすいBOUNDマクロを䜿甚しおいるこずに泚意しおください。
  3. このコヌドブロックは、 disabledActionsフラグをCATransaction蚭定しdisabledActions 。 これにより、各レむダヌの境界の倉曎がすぐに適甚され、アニメヌション化されないこずを確認できたす。 最埌に、 setLayerFramesメ゜ッドがsetLayerFrames 、スラむダヌを目的の堎所に移動したす。


スラむダヌの動きを実装したしたが、それでもコンポヌネントずの察話の終了を凊理する必芁がありたす。 CERangeSlider.mに次のメ゜ッドを远加したす。

 - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { _lowerKnobLayer.highlighted = _upperKnobLayer.highlighted = NO; [_lowerKnobLayer setNeedsDisplay]; [_upperKnobLayer setNeedsDisplay]; } 

このコヌドは、䞡方のスラむダヌを非アクティブ状態に戻したす。 プロゞェクトを起動しお、新しいスラむダヌで少し遊んでください スクリヌンショットず同じように芋えるはずです

画像

コンポヌネントの境界を越えお指を動かし、メ゜ッドの実行を䞭断せずに境界に戻るこずができるこずに気付くかもしれたせん。 これは、小さな画面ず䜎粟床のスタむラスを備えたデバむスの䜿いやすさの非垞に重芁な機胜です理解できない堎合は、指に぀いお話したす。 ]

倉曎通知


したがっお、ナヌザヌが範囲の䞊限ず䞋限を蚭定できるむンタラクティブなコントロヌルがありたす。 しかし、アプリケヌションで䜿甚できるように、これらの倉曎を残りのコヌドに関連付ける方法はありたすか NSNotification 、 Key-Value-ObservingKVO 、委任テンプレヌト、Target-Actionテンプレヌトなど、倉曎の通知に䜿甚できる倚くの異なるオプションがありたす。

UIKitのコンポヌネントを芋るず、 NSNotificationたたはKVOを䜿甚しおいないこずがわかりたすUIKitずの互換性のために、これら2぀のオプションを攟棄する必芁がありたす。 UIKit 、他の2぀のテンプレヌト-委任ずTarget- UIKitが非垞によく䜿甚されUIKit 。

䞡方のパタヌンの詳现な分析を行いたす。



2぀のパタヌンの䞻な違いは次のずおりです。


コンポヌネントには、通知が必芁な可胜性のある状態やむベントが倚くありたせん。圹割を果たすのは、スラむダヌの最倧倀ず最小倀だけです。

この状況では、Target-Actionテンプレヌトの䜿甚が最も正圓化されたす。これがUIControl、レッスンの最初に継承した理由の1぀です。

スラむダヌの読み取り倀はmethod内で曎新されるcontinueTrackingWithTouch:withEvent:ため、ここで通知メカニズムを実装する必芁がありたす。CERangeSlider.mを開き、メ゜ッドcontinueTrackingWithTouch:withEventを芋぀けお、前に次のコヌドを远加したすreturn YES。

 [self sendActionsForControlEvents:UIControlEventValueChanged]; 

倉曎の必芁な目暙を通知するために必芁なのはこれだけです。たあ、それは予想よりも簡単でしたCEViewController.mを

開き、メ゜ッドの最埌に次のコヌドを远加したす。viewDidLoad

 [_rangeSlider addTarget:self action:@selector(slideValueChanged:) forControlEvents:UIControlEventValueChanged]; 

このコヌドは、slideValueChangedスラむダヌがむベントを送信するたびにメ゜ッドを実行しUIControlEventValueChangedたす。

次のメ゜ッドをCEViewController.mに远加したす。

 - (void)slideValueChanged:(id)control { NSLog(@"Slider value changed: (%.2f,%.2f)", _rangeSlider.lowerValue, _rangeSlider.upperValue); } 

このメ゜ッドは、すべおが正垞に機胜するこずの蚌拠ずしお、スラむダヌのすべおの倀をログに送信したす。アプリケヌションを起動し、スラむダヌのスラむダヌを移動したす。座暙の倀がログに衚瀺されたす

画像

恐ろしいフルヌツサラダのように芋えるカラフルなむンタヌフェむスに既にうんざりしおいたす。コンポヌネントに正しい倖芳を䞎える時が来たした

Core Graphicsを䜿甚しおコンポヌネントの倖芳を倉曎する


たず、スラむダヌが移動するプログレスバヌの倖芳を曎新したしょう。CERangeSliderグルヌプで、CALayerのサブクラスである新しいCERangeSliderTrackLayerクラスを远加したす。CERangeSliderTrackLayer.hを開き、その内容を次のものに眮き換えたす。



 #import <QuartzCore/QuartzCore.h> @class CERangeSlider; @interface CERangeSliderTrackLayer : CALayer @property (weak) CERangeSlider* slider; @end 

このコヌドは、スラむダヌレむダヌに察しお行ったこずに基づいお、スラむダヌぞのリンクを远加したす。CERangeSlider.mを開き、次を远加したす#import。

 #import "CERangeSliderTrackLayer.h" 

すぐ䞋の倉数を芋぀け、_trackLayerそのタむプを䜜成したクラスに倉曎したす。

 CERangeSliderTrackLayer* _trackLayer; 

メ゜ッドinitWithFrame:を芋぀けお、レむダヌ䜜成コヌドを曎新したす。

 _trackLayer = [CERangeSliderTrackLayer layer]; _trackLayer.slider = self; [self.layer addSublayer:_trackLayer]; _upperKnobLayer = [CERangeSliderKnobLayer layer]; _upperKnobLayer.slider = self; [self.layer addSublayer:_upperKnobLayer]; _lowerKnobLayer = [CERangeSliderKnobLayer layer]; _lowerKnobLayer.slider = self; [self.layer addSublayer:_lowerKnobLayer]; 

このコヌドにより、新しいストリップが䜿甚され、vyrviglazny色が背景に適甚されなくなるこずを確認できたす。 ]

もう少しやるこずは残っおいたす-コンポヌネントから赀い背景を削陀したす。CEViewController.mを開き、メ゜ッドで次のコヌド行を芋぀けおviewDidLoad消去したす。

 _rangeSlider.backgroundColor = [UIColor redColor]; 

アプリケヌションをビルドしお実行したす...䜕が芋えたすか

画像

䜕もないいいね

あなたが尋ねる-それに぀いお䜕がそんなに玠晎らしいですかハヌドワヌクの成果はすべおなくなりたした心配しないでください-䜿甚したレむダヌに適甚された明るい色を削陀しただけです。コンポヌネントはただ配眮されおいたすが、透明になりたした。

ほずんどの開発者は、コンポヌネントを特定の各アプリケヌションのスタむルにカスタマむズできるこずを奜むため、スラむダヌの倖芳を担圓するいく぀かのプロパティを远加したす。CERangeSlider.hを開き、次のコヌドを远加したす。

 @property (nonatomic) UIColor* trackColour; @property (nonatomic) UIColor* trackHighlightColour; @property (nonatomic) UIColor* knobColour; @property (nonatomic) float curvaceousness; - (float) positionForValue:(float)value; 

さたざたな色のプロパティの目的は非垞に明癜です。に関しおはcurvaceousness-少し埌で調べおください。そしお最埌にpositionForValue:。すでにこのメ゜ッドを実装するこずができたしたが、さたざたなレむダヌで䜿甚できるようになりたした。

色のプロパティの初期倀を远加する必芁がありたす。CERangeSlider.mを開きinitWithFrame:,、残りの倉数の初期化を担圓するコヌドブロックの䞋のメ゜ッドに次のコヌドを远加したす。

 _trackHighlightColour = [UIColor colorWithRed:0.0 green:0.45 blue:0.94 alpha:1.0]; _trackColour = [UIColor colorWithWhite:0.9 alpha:1.0]; _knobColour = [UIColor whiteColor]; _curvaceousness = 1.0; _maximumValue = 10.0; _minimumValue = 0.0; 

CERangeSliderTrackLayer.mを開き、次を远加したす#import。

 #import "CERangeSlider.h" 

このレむダヌには、䞡方のスラむダヌが配眮されおいるストリップが衚瀺されたす。珟時点では、CALayer単色のみを䜿甚できるようにするクラスを継承しおいたす。

ストリップを正しく描画するには、メ゜ッドを実装しdrawInContext:、CoreGraphics APIを䜿甚しおレンダリングする必芁がありたす。

泚Core Graphicsの詳现に぀いおはこのレッスンの範囲倖であるため、Core Graphicsの詳现に぀いおは、Core Graphics 101チュヌトリアルシリヌズのコヌスをお勧めしたす。


次のメ゜ッドをCERangeSliderTrackLayer.mの䞋に远加したす@implementation。

 - (void)drawInContext:(CGContextRef)ctx { // clip float cornerRadius = self.bounds.size.height * self.slider.curvaceousness / 2.0; UIBezierPath *switchOutline = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius]; CGContextAddPath(ctx, switchOutline.CGPath); CGContextClip(ctx); // 1) fill the track CGContextSetFillColorWithColor(ctx, self.slider.trackColour.CGColor); CGContextAddPath(ctx, switchOutline.CGPath); CGContextFillPath(ctx); // 2) fill the highlighed range CGContextSetFillColorWithColor(ctx, self.slider.trackHighlightColour.CGColor); float lower = [self.slider positionForValue:self.slider.lowerValue]; float upper = [self.slider positionForValue:self.slider.upperValue]; CGContextFillRect(ctx, CGRectMake(lower, 0, upper - lower, self.bounds.size.height)); // 3) add a highlight over the track CGRect highlight = CGRectMake(cornerRadius/2, self.bounds.size.height/2, self.bounds.size.width - cornerRadius, self.bounds.size.height/2); UIBezierPath *highlightPath = [UIBezierPath bezierPathWithRoundedRect:highlight cornerRadius:highlight.size.height * self.slider.curvaceousness / 2.0]; CGContextAddPath(ctx, highlightPath.CGPath); CGContextSetFillColorWithColor(ctx, [UIColor colorWithWhite:1.0 alpha:0.4].CGColor); CGContextFillPath(ctx); // 4) inner shadow CGContextSetShadowWithColor(ctx, CGSizeMake(0, 2.0), 3.0, [UIColor grayColor].CGColor); CGContextAddPath(ctx, switchOutline.CGPath); CGContextSetStrokeColorWithColor(ctx, [UIColor grayColor].CGColor); CGContextStrokePath(ctx); // 5) outline the track CGContextAddPath(ctx, switchOutline.CGPath); CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor); CGContextSetLineWidth(ctx, 0.5); CGContextStrokePath(ctx); } 

この画像では、すべおのコヌドブロックがコメントで区切られお結合されおいるこずがわかりたす。

画像

番号付きのセクションは番号付きのコメントに察応しおいたす。

  1. ストリップの圢状が描画された埌、その背景が塗り぀ぶされたす。
  2. 遞択した範囲が匷調衚瀺されたす。
  3. 远加の照明が远加され、ストリップにボリュヌムが远加されたす。
  4. 圱が描かれたす。
  5. ストリップのボリュヌム゚ッゞが描画されたす。


すべおが段階的に分解されたので、CERangeSliderのさたざたなプロパティがその倖芳にどのように圱響するかを確認できたす。

アプリを起動したす。次のよう

画像

になりたす。入力したプロパティの倀をいろいろ詊しお、スラむダヌの倖芳にどのように圱響するかを確認したす。ただプロパティが䜕をしおいるのか興味があるならcurvaceousness、今がそれを詊す時です

スラむダヌを描画するには、同様のアプロヌチを䜿甚したす。CERangeSliderKnobLayer.mを開き、次を远加したす#import。

 #import "CERangeSlider.h" 


次のメ゜ッドを远加したす。

 - (void)drawInContext:(CGContextRef)ctx { CGRect knobFrame = CGRectInset(self.bounds, 2.0, 2.0); UIBezierPath *knobPath = [UIBezierPath bezierPathWithRoundedRect:knobFrame cornerRadius:knobFrame.size.height * self.slider.curvaceousness / 2.0]; // 1) fill - with a subtle shadow CGContextSetShadowWithColor(ctx, CGSizeMake(0, 1), 1.0, [UIColor grayColor].CGColor); CGContextSetFillColorWithColor(ctx, self.slider.knobColour.CGColor); CGContextAddPath(ctx, knobPath.CGPath); CGContextFillPath(ctx); // 2) outline CGContextSetStrokeColorWithColor(ctx, [UIColor grayColor].CGColor); CGContextSetLineWidth(ctx, 0.5); CGContextAddPath(ctx, knobPath.CGPath); CGContextStrokePath(ctx); // 3) inner gradient CGRect rect = CGRectInset(knobFrame, 2.0, 2.0); UIBezierPath *clipPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:rect.size.height * self.slider.curvaceousness / 2.0]; CGGradientRef myGradient; CGColorSpaceRef myColorspace; size_t num_locations = 2; CGFloat locations[2] = { 0.0, 1.0 }; CGFloat components[8] = { 0.0, 0.0, 0.0 , 0.15, // Start color 0.0, 0.0, 0.0, 0.05 }; // End color myColorspace = CGColorSpaceCreateDeviceRGB(); myGradient = CGGradientCreateWithColorComponents (myColorspace, components, locations, num_locations); CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)); CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)); CGContextSaveGState(ctx); CGContextAddPath(ctx, clipPath .CGPath); CGContextClip(ctx); CGContextDrawLinearGradient(ctx, myGradient, startPoint, endPoint, 0); CGGradientRelease(myGradient); CGColorSpaceRelease(myColorspace); CGContextRestoreGState(ctx); // 4) highlight if (self.highlighted) { // fill CGContextSetFillColorWithColor(ctx, [UIColor colorWithWhite:0.0 alpha:0.1].CGColor); CGContextAddPath(ctx, knobPath.CGPath); CGContextFillPath(ctx); } } 

繰り返したすが、このメ゜ッドの結果を分析したす。

  1. スラむダヌの圢状の準備ができるずすぐに、スラむダヌの背景が塗り぀ぶされたす。
  2. さらに、スラむダヌの境界線が描画されたす。
  3. 軜いグラデヌションが远加されたす。
  4. そしお最埌-スラむダヌが匷調衚瀺されおいる堎合-スラむダヌが移動されおいる堎合はそのたた-灰色になりたす。


アプリケヌションを再床実行したす

画像

。CoreGraphicsを䜿甚しおコンポヌネントをレンダリングするこずは努力する䟡倀があるこずがわかりたす。このフレヌムワヌクを䜿甚するず、グラフィックリ゜ヌスを䜿甚するよりもはるかにカスタマむズ可胜で柔軟なコントロヌルを䜜成できたす。

コンポヌネントプロパティの倉曎の凊理


それで、私たちは䜕をしなければなりたせんかコンポヌネントはかなりキャッチヌに芋え、倖芳は完党にカスタマむズ可胜で、Target-Actionテンプレヌトをサポヌトしおいたす。

スラむダヌのプロパティの1぀が、画面に描画された埌にアプリケヌションコヌドで倉曎されるずどうなるかを考えおください。たずえば、スラむダヌの倀の範囲を倉曎したり、スラむダヌのハむラむトを倉曎したりできたす。珟時点では、これらの機胜のサポヌトは実装されおいたせん。これをコヌドに远加する必芁がありたす。

倖郚で蚭定されおいるコンポヌネントプロパティを刀断するには、セッタヌメ゜ッドを蚘述する必芁がありたす。最初の掚枬は次のコヌドである可胜性が高いです。

 - (void)setTrackColour:(UIColor *)trackColour { if (_trackColour != trackColour) { _trackColour = trackColour; [_trackLayer setNeedsDisplay]; } } 

プロパティが倉曎されるずtrackColor、このコヌドブロックは、スラむダヌバヌレむダヌに曎新が必芁であるこずを通知したす。しかし、スラむダヌAPIが8぀の倉数を䜿甚するずいう事実を考えるず、同じコヌドを䜕床も曞き換えるこずは最良の解決策ではありたせん。

マクロを䜿甚する機䌚のようですCERangeSlider.mを開き、initWithFrameメ゜ッドの䞊に次のコヌドを远加したす。

 #define GENERATE_SETTER(PROPERTY, TYPE, SETTER, UPDATER) \ - (void)SETTER:(TYPE)PROPERTY { \ if (_##PROPERTY != PROPERTY) { \ _##PROPERTY = PROPERTY; \ [self UPDATER]; \ } \ } 

このコヌドブロックは、4぀のパラメヌタヌを受け取り、それらを䜿甚しお合成プロパティずそのセッタヌメ゜ッドを生成するマクロを定矩したす。このコヌドの䞋に、別のブロックを远加したす。

 GENERATE_SETTER(trackHighlightColour, UIColor*, setTrackHighlightColour, redrawLayers) GENERATE_SETTER(trackColour, UIColor*, setTrackColour, redrawLayers) GENERATE_SETTER(curvaceousness, float, setCurvaceousness, redrawLayers) GENERATE_SETTER(knobColour, UIColor*, setKnobColour, redrawLayers) GENERATE_SETTER(maximumValue, float, setMaximumValue, setLayerFrames) GENERATE_SETTER(minimumValue, float, setMinimumValue, setLayerFrames) GENERATE_SETTER(lowerValue, float, setLowerValue, setLayerFrames) GENERATE_SETTER(upperValue, float, setUpperValue, setLayerFrames) - (void) redrawLayers { [_upperKnobLayer setNeedsDisplay]; [_lowerKnobLayer setNeedsDisplay]; [_trackLayer setNeedsDisplay]; } 

䞀床にすべおの倉数のセッタヌメ゜ッドを生成したす。このメ゜ッドredrawLayersは、コンポヌネントの倖芳に関連付けられた倉数、およびsetLayerFramesマヌクアップを担圓する倉数に察しお呌び出されたす。

スラむダヌがそのプロパティの倉曎に適切に応答するために必芁なのはこれだけです。

ずにかく、新しいマクロをテストし、すべおが正垞に機胜するこずを確認するために、さらにコヌドを远加する必芁がありたす。CEViewController.mを開き、メ゜ッドの最埌に次のコヌドを远加したすviewDidLoad。

 [self performSelector:@selector(updateState) withObject:nil afterDelay:1.0f]; 

この行はupdateState、2番目の遅延埌にメ゜ッドを呌び出したす。このメ゜ッドをCEViewController.mに远加したす。

 - (void)updateState { _rangeSlider.trackHighlightColour = [UIColor redColor]; _rangeSlider.curvaceousness = 0.0; } 

このメ゜ッドは、ストリップの色を青から赀に、スラむダヌの圢状を正方圢に倉曎したす。プロゞェクトを実行し、スラむダヌの圢状がこれから

画像



画像

泚远加したコヌドは、カスタムコンポヌネントの開発における最も興味深いそしお、ちなみに、開発者によっお忘れられがちな偎面の1぀であるテストを明確に瀺しおいたす。

コントロヌルを開発するずきは、そのプロパティのすべおの可胜な倀ず、コンポヌネントの倖芳に察するそれらの圱響-あなたの責任を確認しおください。良い方法は、いく぀かのボタンずスラむダヌを远加するこずです。各ボタンずスラむダヌは、コンポヌネントのいく぀かのプロパティを担圓したす。この方法により、コヌドを倉曎するこずで気を散らすこずなくコントロヌルをテストできたす。


次は


これでスラむダヌは完党に機胜し、あらゆるアプリケヌションで䜿甚できるようになりたしたカスタムコンポヌネントの䞻な利点の1぀は、さたざたな開発者がさたざたなアプリケヌションで䜿甚できるこずです。

さお、スラむダヌの初挔の準備はできおいたすか

そうでもない。さらにいく぀かのタスクを完了する必芁がありたす

ドキュメント。すべおのプログラマヌのお気に入りの嚯楜。 ]コヌドは完璧で、远加のドキュメントは必芁ないず思いたすが、他の開発者の意芋は劇的に異なる堎合がありたす。すべおのドキュメントを他のコヌド開発者に公開するこずをお勧めしたす。少なくずも、これは公開されおいるすべおのクラスずそのプロパティの説明です。たずえば、あなたのCERangeSliderは、以䞋の曞類が必芁です-目的倉数の説明をmax、min、upper、lower。

信頌性upperValueより倧きい倀を蚭定するずどうなりたすかmaximumValueもちろん、あなた自身がこれを行うこずは決しおありたせん-これは少なくずも愚かです。しかし、他の誰かが詊みるこずを保蚌するこずはできたせん開発者の愚かさのレベルに関係なく、コンポヌネントが垞に適切に機胜するこずを確認する必芁がありたす。

API構造。信頌性に関する以前のポむントは、APIの構造ずいうより広範なトピックに密接に関連しおいたす。柔軟で盎感的で信頌性の高い構造を䜜成するこずで、コンポヌネントが広く䜿甚され、普及するのに圹立ちたす。私の䌚瀟であるShinobiControlsでは、APIの现郚に぀いお議論するのに䜕時間も費やすこずができたす APIの構造は非垞に深いトピックであり、このレッスンの範囲を超えおいたす。このトピックに興味がある堎合は、Matt Gemmellの25のAPI蚭蚈ルヌルを読むこずをお勧めしたす。

コンポヌネントの配垃を開始できる堎所はたくさんありたす。以䞋にいく぀かのオプションを瀺したす。



カスタムコントロヌルの開発に興味があり、おそらく独自のコンポヌネントを䜜成するずいうアむデアに觊発されたこずを願っおいたす。

プロゞェクトの゜ヌスコヌドは、開発の段階に察応するコミットの履歎ずずもにGitHubで公開されたす。迷子になった堎合でも、最埌に完了した段階から簡単に䜜業を続けるこずができたす ]

このリンクからプロゞェクト党䜓をダりンロヌドできたす。

Source: https://habr.com/ru/post/J212111/


All Articles