【问题标题】:How to make a circular UIPickerView (iPhone/iPad)?如何制作圆形 UIPickerView (iPhone/iPad)?
【发布时间】:2012-05-30 11:31:40
【问题描述】:

我已经对这个问题进行了很多研究,但是我仍然不清楚这是否可能。本质上,我想做的是创建一个连续的UIPickerView,因为您可以永远旋转它并且永远不会到达它的末尾(因为最后一个值后面跟着第一个值)。

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

我所追求的是一种创建真正无限的 UIPickerView 的方式,即如果您连续滚动数天、数周、数月或数年,您将永远不会到达终点。如果解决方案是黑客攻击,我不太介意,因为我知道 Apple 尚未提供实现该效果的方法。

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

【问题讨论】:

    标签: iphone ios uipickerview


    【解决方案1】:

    我真的认为,您可以使用本机 UIPickerView 进行的唯一破解如下所述:

    How do you make an UIPickerView component wrap around?

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

    我看到了用 cocos2d 实现的选择器,它是基于 OpenGL 的。我认为,如果你真的需要,你可以尝试使用 UIKit 来实现。

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

    【讨论】:

    • 放置 NSIntegerMax 通常会使程序崩溃。就可用性和记忆力而言,我觉得 Olie 的答案是一个更好、更合理的答案。
    【解决方案2】:

    找到自定义的UIPickerView 类,它工作得很好并且很容易实现 here

    【讨论】:

      【解决方案3】:

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

      希望这行得通! :) 祝你好运!

      【讨论】:

      • 看起来不错。 @The Crazy Chimp 让我们知道它是否有效。
      【解决方案4】:

      没有一个可以包装的原生选择器(无论如何都不是 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];
          }
      }
      

      当然,您必须根据自己的需要进行调整和调整,但这是它的基本要点。

      祝你好运!

      【讨论】:

        【解决方案5】:

        嗯.. 我一直在环顾四周,有几个链接指向一些可能性,最突出的是这个:The Abusive Pickerview,基本思想是您使用 3 组数据填充选择器视图并开始和中心设置。每当用户滚动到顶部或底部集的中心时,您将行值设置回中心集的中心!它似乎比列出一个很长的清单更真实,这会产生无限的错觉。对于解决无限pickerview问题,此解决方案可能是最有效和最简单的!希望这有帮助!

        【讨论】:

        【解决方案6】:

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

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

        所以解决办法是更新pickerView:titleForRow:forComponent:中的数据,然后调用相应的方法重新加载数据,这样pickerview再滚动一个tick时它就在那里。

        【讨论】:

          【解决方案7】:

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

          只需使用 Morion 的方法处理大量具有重复内容的项目,然后每次停止滚动时,重置行。任何人都知道你作弊的唯一方法是,如果他们连续滚动而不中断,直到它停止,从不调用 didSelectRow 函数。这需要一些奉献精神!

          不管你是怎么做的,在我看来它会有点hacky,而且这一定是最简单的hack...

          【讨论】:

            【解决方案8】:

            我做了一个基于UIScrollView的循环tableView。并且基于这个tableView,我重新实现了UIPickerView。你可能对此感兴趣。而且这个picker view拥有UIPickerView的所有特性,还提供了很多新的特性,自定义这个picker view就容易多了。

            https://github.com/danleechina/DLPickerView

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

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

                一个非常简单的解决方案,它会让你认为选择器视图没有尽头,通过放大数组并使模数从数组中选择对应的项目

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-25
                  • 2014-07-17
                  • 1970-01-01
                  • 2012-05-03
                  相关资源
                  最近更新 更多