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

问题描述 投票:21回答:10

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

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

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

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

iphone ios uipickerview
10个回答
4
投票

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

How do you make an UIPickerView component wrap around?

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

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

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


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

3
投票

找到自定义的UIPickerView类,它非常有效并且很容易实现here


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

希望这个有效!! :) 祝好运!


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

当然,你必须调整和调整以适应你的需要,但这是它的基本要点。

祝好运!


0
投票

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


0
投票

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

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

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


0
投票

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

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

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


0
投票

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

https://github.com/danleechina/DLPickerView

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


0
投票

我用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

}

  }
© www.soinside.com 2019 - 2024. All rights reserved.