【问题标题】:UIPickerView that looks like UIDatePicker but with secondsUIPickerView 看起来像 UIDatePicker 但有几秒钟
【发布时间】:2012-06-15 12:10:40
【问题描述】:

我正在制作一个计时器应用程序,需要向用户显示小时、分钟和秒。我尝试使用UIDatePicker,但它只显示小时和分钟作为选择。不是秒。在网上做了一些研究后,我发现没有办法在UIDatePicker 中获得秒数,我不得不从头开始编写自己的UIPickerView

所以我的问题是,是否有这样的示例代码,即有人写了一个CustomUIPickerView 的小时、分钟和秒,我可以将其合并到我的项目中? UIDatePicker 有一个很好的 Hours & Mins 文本覆盖,当用户旋转拨号盘时,它会保持不变。如果有人在他们的自定义选择器中也添加了它,那就太好了。如果没有必要,我不想从头开始编写自定义UIPickerView。谢谢。

【问题讨论】:

  • 使用 uiPickerView 和 3 个组件,每个组件都有数组。 HourArray 从(1 到 12)。 MinutesArray 与(1 到 60)。 secondsArray (1 到 60)
  • @safecase。我从技术上理解它需要如何完成。我只想问是否有人愿意与我分享这段代码,而不是我从头开始编写它
  • @EmilioPelaez - 请不要使用“我们”。您在这里不代表整个 iOS 开发社区。有很多优秀的开发人员乐于分享知识和代码。如果我最终编写了代码,我将在这里与社区的其他人以及任何可能对未来感兴趣并阅读我的帖子的人分享
  • 我说“我们”是因为 Stack Overflow 有一个非常明确的目的,而不是请求代码(如果有人拥有该代码并想分享它,有很多可搜索的方法来做到这一点)。 “我们”在这个前提下。

标签: iphone ios xcode ios5


【解决方案1】:

大家好,这是在您的UIPickerView 中获取小时/分钟/秒的代码。您可以添加 3 个标签,并有策略地将它们放置在选择器上。我也附上了一张图片。

如果您喜欢这个答案,请给它评分!优秀的开发人员分享知识,而不是像某些人建议的那样隐藏它。玩得开心编码!

在你的头 .h 文件中放这个

@interface v1AddTableViewController : UITableViewController
{

    IBOutlet UIPickerView *pickerView;    
    NSMutableArray *hoursArray;
    NSMutableArray *minsArray;
    NSMutableArray *secsArray;

    NSTimeInterval interval;

}

@property(retain, nonatomic) UIPickerView *pickerView;
@property(retain, nonatomic) NSMutableArray *hoursArray;
@property(retain, nonatomic) NSMutableArray *minsArray;
@property(retain, nonatomic) NSMutableArray *secsArray;

在你的 .m 文件中放这个

@synthesize pickerView;
@synthesize hoursArray;
@synthesize minsArray;
@synthesize secsArray;
@synthesize interval;

- (void)viewDidLoad
{
    [super viewDidLoad];


    //initialize arrays
    hoursArray = [[NSMutableArray alloc] init];
    minsArray = [[NSMutableArray alloc] init];
    secsArray = [[NSMutableArray alloc] init];
    NSString *strVal = [[NSString alloc] init];

    for(int i=0; i<61; i++)
    {
        strVal = [NSString stringWithFormat:@"%d", i];

        //NSLog(@"strVal: %@", strVal);

        //Create array with 0-12 hours
        if (i < 13)
        {
            [hoursArray addObject:strVal];
        }

        //create arrays with 0-60 secs/mins
        [minsArray addObject:strVal];
        [secsArray addObject:strVal];
    }


    NSLog(@"[hoursArray count]: %d", [hoursArray count]);
    NSLog(@"[minsArray count]: %d", [minsArray count]);
    NSLog(@"[secsArray count]: %d", [secsArray count]);

}


//Method to define how many columns/dials to show
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 3;
}


// Method to define the numberOfRows in a component using the array.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent :(NSInteger)component 
{ 
    if (component==0)
    {
        return [hoursArray count];
    }
    else if (component==1)
    {
        return [minsArray count];
    }
    else
    {
        return [secsArray count];
    }

}


// Method to show the title of row for a component.
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{

    switch (component) 
    {
        case 0:
            return [hoursArray objectAtIndex:row];
            break;
        case 1:
            return [minsArray objectAtIndex:row];
            break;
        case 2:
            return [secsArray objectAtIndex:row];
            break;    
    }
    return nil;
}


-(IBAction)calculateTimeFromPicker
{

    NSString *hoursStr = [NSString stringWithFormat:@"%@",[hoursArray objectAtIndex:[pickerView selectedRowInComponent:0]]];

    NSString *minsStr = [NSString stringWithFormat:@"%@",[minsArray objectAtIndex:[pickerView selectedRowInComponent:1]]];

    NSString *secsStr = [NSString stringWithFormat:@"%@",[secsArray objectAtIndex:[pickerView selectedRowInComponent:2]]];

    int hoursInt = [hoursStr intValue];
    int minsInt = [minsStr intValue];
    int secsInt = [secsStr intValue];


    interval = secsInt + (minsInt*60) + (hoursInt*3600);

    NSLog(@"hours: %d ... mins: %d .... sec: %d .... interval: %f", hoursInt, minsInt, secsInt, interval);

    NSString *totalTimeStr = [NSString stringWithFormat:@"%f",interval];

}

【讨论】:

  • 如何在选择器中显示“小时”、“分钟”、“秒”?
  • 这一定是最糟糕的做事方式之一...为什么要预先分配所有字符串!!??它也没有设置所需的标签来显示“小时”、“分钟”、“秒”,如屏幕截图所示
【解决方案2】:

与上面公认的解决方案不同,我想出了一些不那么糟糕的方法。绝对不是完美的,但它具有预期的效果(我只在 iPhone 上的 iOS 7 中测试过,只适用于所写的纵向)。欢迎进行编辑以使其更好。相关显示代码如下:

// assumes you conform to UIPickerViewDelegate and UIPickerViewDataSource in your .h
- (void)viewDidLoad
{
    [super viewDidLoad];

    // assumes global UIPickerView declared. Move the frame to wherever you want it
    picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 200)];
    picker.dataSource = self;
    picker.delegate = self;

    UILabel *hourLabel = [[UILabel alloc] initWithFrame:CGRectMake(42, picker.frame.size.height / 2 - 15, 75, 30)];
    hourLabel.text = @"hour";
    [picker addSubview:hourLabel];

    UILabel *minsLabel = [[UILabel alloc] initWithFrame:CGRectMake(42 + (picker.frame.size.width / 3), picker.frame.size.height / 2 - 15, 75, 30)];
    minsLabel.text = @"min";
    [picker addSubview:minsLabel];

    UILabel *secsLabel = [[UILabel alloc] initWithFrame:CGRectMake(42 + ((picker.frame.size.width / 3) * 2), picker.frame.size.height / 2 - 15, 75, 30)];
    secsLabel.text = @"sec";
    [picker addSubview:secsLabel];

    [self.view addSubview:picker];
}

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 3;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    if(component == 0)
        return 24;

    return 60;
}

- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
    return 30;
}

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
    UILabel *columnView = [[UILabel alloc] initWithFrame:CGRectMake(35, 0, self.view.frame.size.width/3 - 35, 30)];
    columnView.text = [NSString stringWithFormat:@"%lu", (long) row];
    columnView.textAlignment = NSTextAlignmentLeft;

    return columnView;
}

结果:

【讨论】:

  • 您是否尝试过使用自动布局?我想知道它将如何考虑设备旋转、不同的屏幕尺寸等。干得好!
  • 我没有。按照现在的写法,它几乎只适用于 iPhone 纵向视图,所以肯定有一些限制。
  • 看起来很漂亮。这个可以打包分享吗?
  • 我愿意,但我正在努力解决让它在横向模式下看起来正确的问题。当使用选择器作为inputViewUITextField 时,我已经把它记下来了(所以手机会处理更新框架),但不是作为独立视图。在处理方向变化方面,我绝对不是最好的。
  • 这绝对应该高于最佳答案,32 太荒谬了!我认为使用自动布局会更好,尽管正如 Rod 建议的那样,您可以只使用乘数和中心属性。干得好!
【解决方案3】:

快速实施

class TimePickerView: UIPickerView, UIPickerViewDataSource, UIPickerViewDelegate {
    var hour:Int = 0
    var minute:Int = 0


    override init() {
        super.init()
        self.setup()
    }

    required internal init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

    func setup(){
        self.delegate = self
        self.dataSource = self

        /*let height = CGFloat(20)
        let offsetX = self.frame.size.width / 3
        let offsetY = self.frame.size.height/2 - height/2
        let marginX = CGFloat(42)
        let width = offsetX - marginX

        let hourLabel = UILabel(frame: CGRectMake(marginX, offsetY, width, height))
        hourLabel.text = "hour"
        self.addSubview(hourLabel)

        let minsLabel = UILabel(frame: CGRectMake(marginX + offsetX, offsetY, width, height))
        minsLabel.text = "min"
        self.addSubview(minsLabel)*/
    }

    func getDate() -> NSDate{
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "HH:mm"
        let date = dateFormatter.dateFromString(String(format: "%02d", self.hour) + ":" + String(format: "%02d", self.minute))
        return date!
    }

    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 2
    }

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        switch component {
        case 0:
            self.hour = row
        case 1:
            self.minute = row
        default:
            println("No component with number \(component)")
        }
    }

    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if component == 0 {
            return 24
        }

        return 60
    }

    func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 30
    }

    func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView!) -> UIView {
        if (view != nil) {
            (view as UILabel).text = String(format:"%02lu", row)
            return view
        }
        let columnView = UILabel(frame: CGRectMake(35, 0, self.frame.size.width/3 - 35, 30))
        columnView.text = String(format:"%02lu", row)
        columnView.textAlignment = NSTextAlignment.Center

        return columnView
    }

}

【讨论】:

    【解决方案4】:

    我不喜欢不依赖将 UILabel 作为子视图添加到 UIPickerView 的答案,因为如果 iOS 决定某天改变外观,硬编码帧将会中断。

    一个简单的技巧是将小时/分钟/秒标签作为具有 1 行的组件

    小时/分钟/秒标签会远离它们的值,但这没关系。大多数用户甚至不会注意到这一点。自己看:

    【讨论】:

    • 在这种情况下,小时、分钟和秒组件也在滚动。这可以预防吗?
    • 这是一个简单易行的实现,不应该因视图更新而中断
    【解决方案5】:

    我对@Stonz2 的版本做了一些细微的改进。计算标签的y位置也有错误。

    .h 文件在这里

    @interface CustomUIDatePicker : UIPickerView <UIPickerViewDataSource, UIPickerViewDelegate>
    
    @property NSInteger hours;
    @property NSInteger mins;
    @property NSInteger secs;
    
    -(NSInteger) getPickerTimeInMS;
    -(void) initialize;
    
    @end
    

    这里是带有改进的 .m 文件

    @implementation CustomUIDatePicker
    -(instancetype)init {
     self = [super init];
     [self initialize];
     return self;
    }
    
    -(id)initWithCoder:(NSCoder *)aDecoder {
     self = [super initWithCoder:aDecoder];
     [self initialize];
     return self;
    }
    
    -(instancetype)initWithFrame:(CGRect)frame {
     self = [super initWithFrame:frame];
     [self initialize];
     return self;
    }
    
    -(void) initialize {
    self.delegate = self;
    self.dataSource = self;
    
    int height = 20;
    int offsetX = self.frame.size.width / 3;
    int offsetY = self.frame.size.height / 2 - height / 2;
    int marginX = 42;
    int width = offsetX - marginX;
    
    UILabel *hourLabel = [[UILabel alloc] initWithFrame:CGRectMake(marginX, offsetY, width, height)];
    hourLabel.text = @"hour";
    [self addSubview:hourLabel];
    
    UILabel *minsLabel = [[UILabel alloc] initWithFrame:CGRectMake(marginX + offsetX, offsetY, width, height)];
    minsLabel.text = @"min";
    [self addSubview:minsLabel];
    
    UILabel *secsLabel = [[UILabel alloc] initWithFrame:CGRectMake(marginX + offsetX * 2, offsetY, width, height)];
    secsLabel.text = @"sec";
    [self addSubview:secsLabel];
    }
    
    -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    if (component == 0) {
        self.hours = row;
    } else if (component == 1) {
        self.mins = row;
    } else if (component == 2) {
        self.secs = row;
    }
    }
    
    -(NSInteger)getPickerTimeInMS {
    return (self.hours * 60 * 60 + self.mins * 60 + self.secs) * 1000;
    }
    
    -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 3;
    }
    -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
    {
     if(component == 0)
         return 24;
    
     return 60;
    }
    
    - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
    {
    return 30;
    }
    
    -(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
    {
    if (view != nil) {
        ((UILabel*)view).text = [NSString stringWithFormat:@"%lu", row];
        return view;
    }
    UILabel *columnView = [[UILabel alloc] initWithFrame:CGRectMake(35, 0, self.frame.size.width/3 - 35, 30)];
    columnView.text = [NSString stringWithFormat:@"%lu", row];
    columnView.textAlignment = NSTextAlignmentLeft;
    
    return columnView;
    }
    
    @end
    

    【讨论】:

    • 泰!请问我是否只想要 2 列(分钟、秒)我应该改变什么?我尝试稍微编辑一下代码,但无法得到我想要的结果
    • numberOfComponentsInPickerView 必须返回 2。在initialize 中,您应该只创建两个子视图。 offsetX 也应该只是 self.frame.size.width / 2;
    【解决方案6】:

    一种选择是使用 ActionSheetPicker

    https://github.com/skywinder/ActionSheetPicker-3.0

    并利用 ActionSheetMultipleStringPicker。您可以按照 ActionSheetPicker 文档中的示例代码进行操作。

    【讨论】:

      【解决方案7】:

      我这里有一个简单的解决方案,你可以为 UIPickerView 实现 UIPickerViewDataSource 的 3 个方法

          - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
          return 5;
      }
      - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
          switch (component) {
              case 0:
                  return 24;
                  break;
              case 1:
              case 3:
                  return 1;
                  break;
              case 2:
              case 4:
                  return 60;
                  break;
              default:
                  return 1;
                  break;
          }
      }
      
      - (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
      {
          switch (component) {
              case 1:
              case 3:
                  return @":";
                  break;
              case 0:
              case 2:
              case 4:
                  return [NSString stringWithFormat:@"%lu", (long) row];
                  break;
              default:
                  return @"";
                  break;
          }
      }
      

      【讨论】:

        【解决方案8】:

        这是“小时/分钟/秒”叠加的另一种解决方案。它使用来自选取器视图的委托的pickerView(UIPickerView, didSelectRow: Int, inComponent: Int) 方法来更新视图的标签。

        这样,无论屏幕方向如何,它都可以正常工作。 它仍然不是最佳的,但它非常简单。

        func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
            let label = UILabel()
            label.text = String(row)
            label.textAlignment = .center
            return label
        }
        
        func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        
            if let label = pickerView.view(forRow: row, forComponent: component) as? UILabel {
        
                if component == 0, row > 1 {
                    label.text = String(row) + " hours"
                }
                else if component == 0 {
                    label.text = String(row) + " hour"
                }
                else if component == 1 {
                    label.text = String(row) + " min"
                }
                else if component == 2 {
                    label.text = String(row) + " sec"
                }
            }
        }
        

        要在显示选择器视图时显示覆盖,必须以编程方式选择行...

        func selectPickerViewRows() {
        
            pickerView.selectRow(0, inComponent: 0, animated: false)
            pickerView.selectRow(0, inComponent: 1, animated: false)
            pickerView.selectRow(30, inComponent: 2, animated: false)
        
            pickerView(pickerView, didSelectRow: 0, inComponent: 0)
            pickerView(pickerView, didSelectRow: 0, inComponent: 1)
            pickerView(pickerView, didSelectRow: 30, inComponent: 2)
        }
        

        【讨论】:

        • 很好的答案@nyg,但你不应该使用 label.text?.append(" min") 就好像你选择了两次行,你得到了两次单词。更好地使用 label.text = String(row)+" min".. 但是谢谢效果很好
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-03
        • 2012-03-15
        • 1970-01-01
        • 1970-01-01
        • 2018-08-04
        相关资源
        最近更新 更多