如何制作圆形UIPickerView(iPhone / iPad)?

时间:2012-05-30 11:31:41

标签: iphone ios uipickerview

我已经对这个问题进行了很多研究,但是我还不清楚它是否可能。基本上我想做的是创建一个连续的UIPickerView,你可以永久地旋转它,你将永远不会到达它的末尾(因为最后一个值后跟第一个值)。 / p>

我在网上看过,似乎有各种各样的黑客可以达到预期的效果。然而,很多这些解决方案似乎增加了UIPickerView中的行数以诱使用户认为UIPickerView是连续的(但是,实际上如果他们继续滚动它们最终会到达终点)

我所追求的是一种创建UIPickerView的方法,如果你继续滚动数天,数周,数月或数年,你将永远无法达到目的。如果解决方案是黑客攻击,我不介意太多,因为我知道Apple还没有提供实现这种效果的方法。

请有人建议这样做(或至少指出我正确的方向)?

10 个答案:

答案 0 :(得分:4)

我真的认为,你可以用本机UIPickerView做的唯一的黑客在这里描述:

How do you make an UIPickerView component wrap around?

另一种方法是使真正的循环选择器是自己实现它。

我看到使用cocos2d实现的选择器,即基于OpenGL的选择器。我想,如果你真的需要,可以尝试使用UIKit。

或者只是忘记它并使用具有可重复内容的NSIntegerMax行创建一个选择器。我想没有人会把它旋转到最后。

答案 1 :(得分:3)

找到自定义UIPickerView类,该类工作得非常好并且非常容易实现 here

答案 2 :(得分:2)

这是可能的。这是你如何做到的。首先设置一个计时器。我们假设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];
}

在计时器点火方法中,检查uipickerview的任何“边缘”视图是否正在显示。

- (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;
}

希望这个有效!!祝你好运!

答案 3 :(得分:2)

没有包装的原生选择器(不管是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];
    }
}

当然,您必须进行调整和调整以满足您的需求,但这是它的基本要点。

祝你好运!

答案 4 :(得分:0)

嗯..我一直在四处寻找,并且有几个可能性的链接,最突出的是这个:The Abusive Pickerview,基本的想法是你用3套填充挑选视图您的数据和开始以及中心设置。每当用户滚动到顶部或底部集合的中心时,您将行值设置回中心集的中心!它似乎更真实,然后制作一个非常长的列表,将创造无限的幻觉。对于解决无限挑选视图问题,此解决方案可能是最有效和最简单的!希望这有帮助!

答案 5 :(得分:0)

在我看来,需要将大量数据填充到pickerView中的解决方案是浪费的。以下解决方案使用智能而不是内存。

需要填充的唯一行是视图中可见的行以及紧接在第一个之前的行和紧跟在最后一个之后的行。当用户滚动组件时,将为每个滚动的行调用pickerView:titleForRow:forComponent:。

所以解决方案是更新pickerView:titleForRow:forComponent:中的数据,然后调用适当的方法重新加载数据,以便当pickerview再滚动一次时它就在那里。

答案 6 :(得分:0)

我知道这是一个老问题,但我发现它只是在我自己的项目中实现它。

只需使用Morion的大量项目重复内容的方法,然后每次停止滚动,重置行。任何人都会弄清楚你被欺骗的唯一方法是,如果他们连续不断地滚动直到它停止,从不调用didSelectRow函数。需要一些奉献精神!

无论你怎么做,在我看来,它会感觉有点hacky,&amp;这一定是最简单的黑客...

答案 7 :(得分:0)

我基于UIScrollView创建了一个循环表。基于此tableView,我重新实现了UIPickerView。你可能对此感兴趣。此选择器视图具有UIPickerView的所有功能,但也提供了许多新功能,并且自定义此选择器视图更容易。

https://github.com/danleechina/DLPickerView

请注意,此DLPickerView循环滚动实际上是循环滚动。所有的魔法都是因为另一个班级DLTableView而发生的。

答案 8 :(得分:0)

我使用pickerView Selection来创建一个圆形的:

MAIL_ENCRYPTION=

答案 9 :(得分:0)

一个非常简单的解决方案,通过按比例放大数组并使模数从数组中选择相应的项目,你会认为拾取器视图没有结束

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]
}