1

I'm trying to create a UIPicker where I can select minutes and seconds. Here's the code:

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

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

    return 60;
}

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

- (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    [paragraphStyle setAlignment:NSTextAlignmentCenter];
    [paragraphStyle setTailIndent:20];

    NSString *value = [NSString stringWithFormat:@"%d", row];

    return [[NSAttributedString alloc] initWithString:value
                                           attributes:@{NSParagraphStyleAttributeName:paragraphStyle}];
}

- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
    return 160;
}

How can I get rid of the annoying curvature and just make vertical columns?

Also, how can I add "min" and "sec" to the selection row?

picker

soleil
  • 12,133
  • 33
  • 112
  • 183
  • 1
    You can use a UIDatePicker and remove the hassle of coding this yourself. – ZeMoon Sep 05 '14 at 18:15
  • AFAIK You can't do minutes and seconds with a UIDatePicker. – soleil Sep 05 '14 at 18:24
  • 1
    I think you'll have to create your own if you don't want the curvature. It should be easy to just use 2 narrow table views. You would only have to add code in the scroll view delegate methods to have the value automatically be selected when the scrolling stops. – rdelmar Sep 05 '14 at 19:41

3 Answers3

1

I have found that changing the width for the components has an effect on how much offset or curvature effect the UIPickerView applies to the selected row. In your delegate implement pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat and try returning values that are just large enough to show the information you want to display, say 40 or 60 for just two characters of text for a simple number. To change the spacing between columns/components you can add extra components that return 0 for number of rows, and use the width of these empty components to move around and adjust the placement of the components you're displaying data in.

Jim
  • 21
  • 2
0

Since you're making it into 24 mins and 60 secs, I'd fake it with 24h date picker. IMO

How can I get rid of the annoying curvature and just make vertical columns?

You don't. Else, implement your own using 2 table views or something.

Also, how can I add "min" and "sec" to the selection row?

NSString *value;

if(component == 0) value = [NSString stringWithFormat:@"%d min", row];
else  value = [NSString stringWithFormat:@"%d sec", row];
marko
  • 1,721
  • 15
  • 25
  • This code adds "min" and "sec" to ALL rows. I just want the middle "selected" row to show the "min" and "sec" text. – soleil Sep 05 '14 at 18:52
  • And as far as faking it with a 24h date picker, that doesn't seem possible since I need to display min and sec, not hours and min. – soleil Sep 05 '14 at 18:53
  • 24h date picker just show numbers (checks the alarm app, make sure you set the device to 24h). and if you want a single min and sec, make it 4 components to the picker, and return "min" and "sec" for component 1 and 3 respectively, and return 1 row for both of them. – marko Sep 05 '14 at 18:58
0

As others have said, the existing API can't do it. If you want to do this, you'll have to write your own code to do it. I created a horizontal value picker in the past and it was annoying, but fairly straight forward. To do it I used a UILabel, an image and a UIScrollView.

First set up the UI

  1. Take the label and stick it in the scroll view. Populate the labels with the values you want.
  2. Set the scroll view height, width and position to show what you want the user to see. If you want the user to see the selected item and 3 on the top and 3 on the bottom then make it big enough to show 7 lines in the label.
  3. You image is the "selection indicator" you overlay this on top of the center item in the scroll view to show the user this is the selected item.
  4. If you want a pretty fade effect as your values go off the end you can create a white (or whatever background your app is) block which fades to transparent and overlay that over the top and bottom of the scroll view. This creates a fading number effect at the edges of the component.

Visually it show work... for the most part, but we need back end code to support it.

  1. Create a UIScrollViewDelegate and implement scrollViewDidScroll: which will tell you the content offset of your scroll view when the users changes it.
  2. Write a method that will convert the scroll view content offset to which "item" was selected. If you know how many lines are in the label (and you should) and you know how tall the label is (and you should) take the label height/lines to get how tall each "item" is. Then you take the content offset, divide by the item height, floor it and you know which line your are at... while remembering the content offset is from the TOP of your scroll view, so add in half the height. :)

So the basic functionality is done and it works, but it's not pretty.

The selected item didn't end centered in my selection image, which is just visually ugly. But you can fix it easily enough. You know the height of the items in the list and you know which item is selected, so programmatically scroll the wheel to have that item centered.

The other issue I had was momentum. Like a real wheel I wanted my picker to have momentum so if you gave it a hard flick it would go for a while, but if you gave it a gentle flick it would stop pretty fast. I had long list and if people were going from 7 to 70 I wanted them to be able to do it easily. If you are interested in in that, I can show you how I did it and post some code when I get back to the machine with my code on it.

DBD
  • 23,075
  • 12
  • 60
  • 84