iOS 7 UIPickerView non-selected row curvature modification multiple components
Asked Answered
E

4

10

I am trying to modify the UIPickerView curvature to look like the iOS 7 Timer.

Here is what I have: enter image description here

Note how the 2 and 4 are offset significantly from the 3 in the selected row. Is there a way to make it more 'flat', a la Timer in iOS7, where the 2 and 4 are directly above and below the 3?

Here is what I have tried so far:

  1. Play around with - (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component

  2. Use custom UIView and a custom UILabel offset within

None seem to work.

Any help?

Engud answered 7/11, 2013 at 0:24 Comment(2)
How did you add these labels "ft" and "in"? I'm looking for a way to do thatLionellionello
@DCDC - The labels are simply overlays on top of the picker using UILabel.Engud
Q
8

The offset is due to the separate, raised plane on which the selector indicator lies. This is actually a fairly accurate rendering of such a real life perspective (of nested, printed, clear cylinders). What Apple does in the Clock app is to right align the picker labels. This ensures that the spacing between the number and its unit label remains constant. You can do the same thing.

First off, in your picker view delegate you want to implement -pickerView:attributedTitleForRow:forComponent: in lieu of -pickerView:titleForRow:forComponent:. (It looks like you’ve already done this given the white text color.) You will use the NSParagraphStyle string attribute. You will need to set two properties on the paragraph style: alignment and tail indent. The alignment should be set to NSTextRightAlignment. The tail alignment will depend on how far you want the labels to sit from the right edge of the component. If you have more than one component, you will need to set the width of the component using -pickerView:widthForComponent:. If you want to keep the curvature to a minimum, set the component width to approximately double the tail indent.

NOTE: If your picker has exactly two components, the width of each component must be less than ⅓ the width of the picker.

Here is a code example:

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

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

- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
    return 300;
}
Quag answered 7/2, 2014 at 18:49 Comment(4)
I'm sorry, your NSTextAlignment solution did not work. The curvature is still seen quite prominently. Did you verify your solution?Engud
I forgot to mention that for this solution to work with multiple components, the component width needs to be set to roughly twice the tail indent value. I have updated my answer with the details.Quag
Also, to display properly there is a maximum width for components where a picker has exactly two components.Quag
It seems that it was your note that did the trick. Setting the width of both components to 100 was the difference. No need for paragraph styles or indents.Engud
H
2

Instead 2 components, try to make 4 components, similar to this snippet:

- (void)viewDidLoad
{
    [super viewDidLoad];
     self.model  = @[@"1",@"2",@"3",@"4",@"5",@"6"];
}


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

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    NSInteger numberOfRows;
    if(component == 0 || component == 2)
    {
        numberOfRows = [self.model count];
    }
    else
    {
        numberOfRows = 1;
    }
    return numberOfRows;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    NSString* title;
    switch (component) {
        case 0:
        case 2:
            title = self.model[row];
            break;
        case 1:
            title = @"ft";
            break;
        case 3:
            title = @"in";
            break;
        default:
            break;
    }
    return title;
}
Hartebeest answered 7/11, 2013 at 1:44 Comment(5)
The placement of units in separate components is elegant, but how do I prevent the scrolling motion on the units?Engud
Apple didn't provide interface for disabling bounce of picker segment, because it's against it's HIG. By HIG all views should have bounce, because that way user knows it's the end of the road and nothing get stacked. So, we can't disable segment bouncing without some hack, or maybe you can add transparent view over segments with units and disable user interaction on it.Hartebeest
Other solution would be what you already tried, to have 2 pickers and units outside of them, or as a part of values.Hartebeest
@Hartebeest but The units in Apple's clock do not bounce. I am trying to disable the bounce on the units too.Overcareful
found this article if anyone else is still looking medium.com/@luisfmachado/uipickerview-fixed-labels-66f947ded0a8Entozoon
D
1

I fixed the layout problem by using

- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component{
    switch (component){
        case 0:
            return 106.0f;
        case 1:
            return 50.0f;
        case 2:
            return 106.0f;
    }
    return 0;
}

https://mcmap.net/q/455408/-uipickerview-with-custom-views-broken-in-ios7

The link says layout breaks when UIPickerView component's size is greater than 106. So I made my section width 106.

I added the space in the middle by making the UIPicker have 3 components.

Diabolic answered 20/11, 2013 at 23:40 Comment(0)
E
0

I noticed that if the number of components == 1, then there is no such zooming + offset effect.

The hack that I implemented to fix this was to have 2 UIPickerViews on screen and implement the delegate and datasource functions accordingly.

Engud answered 7/11, 2013 at 0:51 Comment(3)
The reason this works is that the picker labels are center aligned. When you set the components to 1, this ensures that the component is center aligned ensuring alignment of perspective. This is not how the problem is handled in the Clock app. If you look closely, you will notice that Apple has right aligned the labels. See my answer for a how-to.Quag
@Endersstocker, your solution did not work for me. Did you verify on iOS 7?Engud
It is an approach designed and used on iOS 7. I believe this is also the approach used by Apple in the Clock app.Quag

© 2022 - 2024 — McMap. All rights reserved.