web-dev-qa-db-ja.com

円形のUIPickerView(iPhone / iPad)を作成するにはどうすればよいですか?

私はこの質問をよく調べましたが、それが可能かどうかはまだわかりません。基本的に私がやりたいのは、永久に回転でき、最後に到達することは決してないという意味で連続的なUIPickerViewを作成することです(最後の値の後に最初の値が続くため)。

私はオンラインで周りを見回しましたが、望ましい効果を達成するためのさまざまなハックがあるようです。ただし、これらのソリューションの多くは、UIPickerViewの行数を増やして、ユーザーをだましてUIPickerViewが連続していると思わせるように見えます(ただし、実際には、スクロールし続けると、最終的には終わり)。

私が求めているのは、数日、数週間、数か月、または数年スクロールし続けると終わりに到達しないという意味で、真に無限のUIPickerViewを作成する方法です。 Appleはまだ効果を達成する方法を提供していないことを理解しているので、解決策がハックであるかどうかはあまり気にしません。

誰かがこれを行う方法についてアドバイスできますか(または少なくとも私を正しい方向に向けてください)?

21
The Crazy Chimp

ネイティブUIPickerViewで実行できる唯一のハックはここで説明されていると私は本当に思います:

IPickerViewコンポーネントをどのようにラップアラウンドさせますか?

本当にループしたピッカーを作成するもう1つの方法は、自分で実装することです。

OpenGLベースのcocos2dで実装されたピッカーを見ました。本当に必要な場合は、UIKitを使用してそれを試すことができると思います。

または、それを忘れて、繰り返し可能なコンテンツを含むNSIntegerMax行でピッカーを作成します。最後まで誰も回さないと思います。

4
Morion

非常にうまく機能し、実装が非常に簡単なカスタムUIPickerViewクラスを見つけます ここ

3
Animesh

それが可能だ。これがあなたがそれをする方法です。まずタイマーを設定します。 int maxNumberが任意の値に設定されたインスタンス変数であると仮定しましょう。

- ( void) viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
   [self.timer invalidate];
   self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self    selector:@selector(timerFireMethod:) userInfo:nil repeats:YES];

}

- (void) viewWillDisappear:(BOOL)animated{
   [self.timer invalidate];
   [super viewWillDisappear:animated];
}

- (void)viewDidLoad
{
 [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
 [self.infinitePickerView selectRow:(int)maxNumber*5 inComponent:0 animated:YES];
}

Timer fireメソッドで、uipickerviewの「Edge」ビューのいずれかが表示されているかどうかを確認します。

- (void)timerFireMethod:(NSTimer*)theTimer{
int rowSelected = [self.infinitePickerView selectedRowInComponent:0];    
for (int i=0; i<= 20; i++) {
    UIView * viewBelow = [self.infinitePickerView viewForRow:i forComponent:0];
    UIView * viewAbove = [self.infinitePickerView viewForRow:maxNumber*10-20+i forComponent:0];
    if(viewBelow!=nil || viewAbove!=nil){
        int middlePosition = maxNumber * 5 + (rowSelected % maxNumber);
        [self.infinitePickerView selectRow:middlePosition inComponent:0 animated:NO];
        break;
    }
}
}

[self.infinitePickerView viewForRow:i forComponent:0];は表示されている場合にのみUIViewを返すため、これが機能することに注意してください。

もちろん、あなたのUIPickerViewDelegateは次のようなものを使用する必要があります

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
   return  maxNumber * 10; //returning a large number
}

 //You cannot use this method
/*
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row  forComponent:(NSInteger)component{}
 You have to use the method below in lieu of it
*/

 - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view{

 UILabel * label;

 if (!view) {
    label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0,  self.infinitePickerView.bounds.size.width, 30)];
}else {
    label = (UILabel *) view;     
}
[label setText:[NSString stringWithFormat:@" %d", row % maxNumber]];
return label;
}

これがうまくいくことを願っています!! :) 幸運を!

2
clearwater82

ラップするネイティブピッカーはありません(とにかくiOS7.1の時点ではありません)。これらの線に沿った何かによってこれを偽造することができます。

NSArray *picks = @[ @"zero", @"one", @"two", @"three" ]; // 4 entries

// ...

- (NSInteger)pickerView:(UIPickerView *)pickerView 
numberOfRowsInComponent:(NSInteger)component
{
    return (3 * [picks count]); // we're very tricky, displaying only
                                // ...the SECOND set of entries, to
                                // ... give the impression of wrap-around
}

// ...

- (NSString *)pickerView:(UIPickerView *)pickerView 
             titleForRow:(NSInteger)row 
            forComponent:(NSInteger)component
{
    // All "3" sets of entries have the same values;
    // this is what gives the appearance of wrap-around.
    int titleRow = row % [picks count];
    return [picks objectAtIndex: titleRow];
}

// ...

- (void)pickerView:(UIPickerView *)pickerView 
      didSelectRow:(NSInteger)row 
       inComponent:(NSInteger)component
{
    // If the wheel turns to outside our 2nd-set range,
    // ...set it to the matching item in the 2nd set.
    // In this way, we always display what looks like a wrap-around.
    int count = [picks count];
    if ((row <  count)
    ||  (row > (count * 2) )
    {
        [pickerView selectRow: (row % count) inComponent: component animated: NO];
    }
}

もちろん、ニーズに合わせて微調整して調整する必要がありますが、これが基本的な要点です。

幸運を!

2
Olie

うーん..私は周りを見回してきましたが、いくつかの可能性へのリンクがいくつかありますが、最も優れているのはこれです: 虐待的なピッカービュー 、基本的な考え方は、ピッカービューに3を入力することですデータとスタートのセットとセンターセット。ユーザーが上部または下部のセットのいずれかの中央にスクロールするたびに、行の値を中央のセットの中央に戻します。無限の幻想を生み出す本当に長いリストを作るよりも本物のようです。このソリューションは、無限ピッカービューの問題を解決する上で最も効果的で簡単な場合があります。これがお役に立てば幸いです。

0
wayway

PickerView Selectionを使用して、円形のものを作成しました。

   import UIKit
   class ViewController:        
 UIViewController,UIPickerViewDelegate,UIPickerViewDataSource
{
@IBOutlet weak var picker: UIPickerView!
@IBOutlet weak var myLabel: UILabel!

let numbers = [0,1,2,3,4,5,6,7,8,9]

override func viewDidLoad()
{
    super.viewDidLoad()
    // A.Select Second Row
    picker.selectRow(1, inComponent: 0, animated: false)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int
{
    return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
{
     //B. Add 2 rows to your array count  
    return numbers.count + 2
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{
    var rowTitle = ""
    switch row
    {
    case 0:
        // C. Set first row title to last component of the array.
         // This row is not visible to the user as it is hidden by the        

         //selection in ViewDidLoad
        rowTitle = "\(numbers.last!)"
    case numbers.count + 1:
        //D. Set last row title to first array component
        rowTitle = "\(numbers.first!)"
    default:
        // E. Remember [row - 1] to avoid errors
        rowTitle = "\(numbers[row - 1])"
    }
    return rowTitle
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
    var theText = ""
    switch row
    {
    case 0:
      //F. Select Row at array count to load the last row
        pickerView.selectRow(numbers.count , inComponent: 0, animated: false)
        theText = = "\(numbers.last!)"
    case numbers.count + 1:
     //G. This Selection will set the picker to initial state
        pickerView.selectRow(1, inComponent: 0, animated: false)
        theText = "\(numbers.first!)"
    default:
        theText = "\(numbers[row - 1])"
    }
    myLabel.text = theText

}

  }
0
Atka

UIScrollViewに基づいて循環tableViewを作成しました。そして、このtableViewに基づいて、UIPickerViewを再実装します。あなたはこれに興味があるかもしれません。そして、このピッカービューには、UIPickerViewのすべての機能がありますが、多くの新機能もあり、このピッカービューのカスタムははるかに簡単です。

https://github.com/danleechina/DLPickerView

また、このDLPickerViewの周期的なスクロールは、実際には周期的にスクロールしていることに注意してください。すべての魔法は別のクラスDLTableViewのために起こりました。

0
Dan Lee

配列をスケールアップし、モジュラスに配列から対応するアイテムを選択させることにより、ピッカービューの終わりがないとuに思わせる非常に単純なソリューション

override func viewDidLoad(){
    super.viewDidLoad()
    self.yourArray.selectRow((yourArray.count*100)/2, inComponent: 0, animated: false)
}

func pickerView(_ pickerView: UIPickerView, numberOfRowaInComponent component: Int) -> Int{
    return yourArray.count*100
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)->String{
    return yourArray[row % yourArray.count]
}
0
Torped

古い質問だとは思いますが、自分のプロジェクトで実装しているときに見つけました。

繰り返しコンテンツを含む多数のアイテムに対してMorionの方法を使用するだけで、スクロールが停止するたびに行をリセットします。だれかがあなたがだまされたことを理解する唯一の方法は、didSelectRow関数を呼び出さずに、停止するまで途切れることなく継続的にスクロールすることです。少しの献身が必要です!

あなたがそれをどのように行うかに関係なく、それは少しハッキーに感じるだろうと私には思えます、そしてこれは最も簡単なハックでなければなりません...

0
SomaMan

大量のデータをpickerViewに詰め込む必要があるソリューションは無駄だと私には思えます。次のソリューションは、メモリの代わりにスマートを使用します。

入力する必要がある行は、ビューに表示されている行と、最初の行の直前の行と最後の行の直後の行だけです。ユーザーがコンポーネントをスクロールすると、スクロールする行ごとにpickerView:titleForRow:forComponent:が呼び出されます。

したがって、解決策は、pickerView:titleForRow:forComponent:のデータを更新してから、適切なメソッドを呼び出してデータを再読み込みし、pickerviewがもう1ティックスクロールしたときにデータがそこにあるようにすることです。

0
Victor Engel