代码之家  ›  专栏  ›  技术社区  ›  squelart

修复了UIPickerView的选择栏中的标签

  •  31
  • squelart  · 技术社区  · 16 年前

    在时钟应用程序中,计时器屏幕显示选择器(可能是 UIPicker 在里面 UIDatePickerModeCountDownTimer

    (编辑)请注意,这些标签是 固定的 :当捡拾轮滚动时,它们不会移动。

    有没有办法在标准的选择栏中显示此类固定标签 UIPickerView 组成部分

    我没有找到任何API可以帮助实现这一点。一项建议是添加一个 UILabel 作为选择器的子视图,但这不起作用。


    答复

    我听从了埃德·马蒂的建议(答案如下),这很有效!不完美,但它应该愚弄人们。作为参考,这里是我的实现,请随意改进。。。

    - (void)viewDidLoad {
        // Add pickerView
        self.pickerView = [[UIPickerView alloc] initWithFrame:CGRectZero];
        [pickerView release];
        CGSize pickerSize = [pickerView sizeThatFits:CGSizeZero];
        CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
        #define toolbarHeight           40.0
        CGFloat pickerTop = screenRect.size.height - toolbarHeight - pickerSize.height;
        CGRect pickerRect = CGRectMake(0.0, pickerTop, pickerSize.width, pickerSize.height);
        pickerView.frame = pickerRect;
    
        // Add label on top of pickerView
        CGFloat top = pickerTop + 2;
        CGFloat height = pickerSize.height - 2;
        [self addPickerLabel:@"x" rightX:123.0 top:top height:height];
        [self addPickerLabel:@"y" rightX:183.0 top:top height:height];
        //...
    }
    
    - (void)addPickerLabel:(NSString *)labelString rightX:(CGFloat)rightX top:(CGFloat)top height:(CGFloat)height {
    #define PICKER_LABEL_FONT_SIZE 18
    #define PICKER_LABEL_ALPHA 0.7
        UIFont *font = [UIFont boldSystemFontOfSize:PICKER_LABEL_FONT_SIZE];
        CGFloat x = rightX - [labelString sizeWithFont:font].width;
    
        // White label 1 pixel below, to simulate embossing.
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, top + 1, rightX, height)];
        label.text = labelString;
        label.font = font;
        label.textColor = [UIColor whiteColor];
        label.backgroundColor = [UIColor clearColor];
        label.opaque = NO;
        label.alpha = PICKER_LABEL_ALPHA;
        [self.view addSubview:label];
        [label release];
    
        // Actual label.
        label = [[UILabel alloc] initWithFrame:CGRectMake(x, top, rightX, height)];
        label.text = labelString;
        label.font = font;
        label.backgroundColor = [UIColor clearColor];
        label.opaque = NO;
        label.alpha = PICKER_LABEL_ALPHA;
        [self.view addSubview:label];
        [label release];
    }
    
    11 回复  |  直到 11 年前
        1
  •  13
  •   dizy    15 年前

    它看起来像这样

    
    UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(135, 93, 80, 30)] autorelease];
    label.text = @"Label";
    label.font = [UIFont boldSystemFontOfSize:20];
    label.backgroundColor = [UIColor clearColor];
    label.shadowColor = [UIColor whiteColor];
    label.shadowOffset = CGSizeMake (0,1);
    [picker insertSubview:label aboveSubview:[picker.subviews objectAtIndex:5]]; 
    //When you have multiple components (sections)...
    //you will need to find which subview you need to actually get under
    //so experiment with that 'objectAtIndex:5'
    //
    //you can do something like the following to find the view to get on top of
    // define @class UIPickerTable;
    // NSMutableArray *tables = [[NSMutableArray alloc] init];
    // for (id i in picker.subviews) if([i isKindOfClass:[UIPickerTable class]]) [tables addObject:i];
    // etc...
    
    

        2
  •  11
  •   Paras Joshi    11 年前

    我已经把dizy的答案分类为 UIPickerView 几年前。刚刚验证了它仍然适用于iOSSDK4.3,并发布在这里。它允许您添加标签(XX小时)并设置对此标签的更改动画(例如1小时->3小时),就像 UIDatePicker

    // UIPickerView_SelectionBarLabelSupport.h
    //
    // This file adds a new API to UIPickerView that allows to easily recreate
    // the look and feel of UIDatePicker labeled components.
    //
    // Copyright (c) 2009, Andrey Tarantsov <andreyvit@gmail.com>
    //
    // Permission to use, copy, modify, and/or distribute this software for any
    // purpose with or without fee is hereby granted, provided that the above
    // copyright notice and this permission notice appear in all copies.
    //
    // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    
    
    #import <Foundation/Foundation.h>
    
    
    // useful constants for your font size-related code
    #define kPickerViewDefaultTitleFontSize 20.0f
    #define kDatePickerTitleFontSize 25.0f
    #define kDatePickerLabelFontSize 21.0f
    
    
    @interface UIPickerView (SelectionBarLabelSupport)
    
    // The primary API to add a label to the given component.
    // If you want to match the look of UIDatePicker, use 21pt as pointSize and 25pt as the font size of your content views (titlePointSize).
    // (Note that UIPickerView defaults to 20pt items, so you need to use custom views. See a helper method below.)
    // Repeated calls will change the label with an animation effect similar to UIDatePicker's one.
    //
    // To call this method on viewDidLoad, please call [pickerView layoutSubviews] first so that all subviews
    // get created.
    - (void)addLabel:(NSString *)label ofSize:(CGFloat)pointSize toComponent:(NSInteger)component leftAlignedAt:(CGFloat)offset baselineAlignedWithFontOfSize:(CGFloat)titlePointSize;
    
    // A helper method for your delegate's "pickerView:viewForRow:forComponent:reusingView:".
    // Creates a propertly positioned right-aligned label of the given size, and also handles reuse.
    // The actual UILabel is a child of the returned view, use [returnedView viewWithTag:1] to retrieve the label.
    - (UIView *)viewForShadedLabelWithText:(NSString *)label ofSize:(CGFloat)pointSize forComponent:(NSInteger)component rightAlignedAt:(CGFloat)offset reusingView:(UIView *)view;
    
    // Creates a shaded label of the given size, looking similar to the labels used by UIPickerView/UIDatePicker.
    - (UILabel *)shadedLabelWithText:(NSString *)label ofSize:(CGFloat)pointSize;
    
    @end
    

    以及实施:

    // UIPickerView_SelectionBarLabelSupport.m
    //
    // This file adds a new API to UIPickerView that allows to easily recreate
    // the look and feel of UIDatePicker labeled components.
    //
    // Copyright (c) 2009, Andrey Tarantsov <andreyvit@gmail.com>
    //
    // Permission to use, copy, modify, and/or distribute this software for any
    // purpose with or without fee is hereby granted, provided that the above
    // copyright notice and this permission notice appear in all copies.
    //
    // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    
    #import "UIPickerView_SelectionBarLabelSupport.h"
    
    
    // used to find existing component labels among UIPicker's children
    #define kMagicTag 89464534
    // a private UIKit implementation detail, but we do degrade gracefully in case it stops working
    #define kSelectionBarClassName @"_UIPickerViewSelectionBar"
    
    // used to sort per-component selection bars in a left-to-right order
    static NSInteger compareViews(UIView *a, UIView *b, void *context) {
        CGFloat ax = a.frame.origin.x, bx = b.frame.origin.x;
        if (ax < bx)
            return -1;
        else if (ax > bx)
            return 1;
        else
            return 0;
    }
    
    
    @implementation UIPickerView (SelectionBarLabelSupport)
    
    - (UILabel *)shadedLabelWithText:(NSString *)label ofSize:(CGFloat)pointSize {
        UIFont *font = [UIFont boldSystemFontOfSize:pointSize];
        CGSize size = [label sizeWithFont:font];
        UILabel *labelView = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)] autorelease];
        labelView.font = font;
        labelView.adjustsFontSizeToFitWidth = NO;
        labelView.shadowOffset = CGSizeMake(1, 1);
        labelView.textColor = [UIColor blackColor];
        labelView.shadowColor = [UIColor whiteColor];
        labelView.opaque = NO;
        labelView.backgroundColor = [UIColor clearColor];
        labelView.text = label;
        labelView.userInteractionEnabled = NO;
        return labelView;
    }
    
    - (UIView *)viewForShadedLabelWithText:(NSString *)title ofSize:(CGFloat)pointSize forComponent:(NSInteger)component rightAlignedAt:(CGFloat)offset reusingView:(UIView *)view {
        UILabel *label;
        UIView *wrapper;
        if (view != nil) {
            wrapper = view;
            label = (UILabel *)[wrapper viewWithTag:1];
        } else {
            CGFloat width = [self.delegate pickerView:self widthForComponent:component];
    
            label = [self shadedLabelWithText:title ofSize:pointSize];
            CGSize size = label.frame.size;
            label.frame = CGRectMake(0, 0, offset, size.height);
            label.tag = 1;
            label.textAlignment = UITextAlignmentRight;
            label.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    
            wrapper = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, width, size.height)] autorelease];
            wrapper.autoresizesSubviews = NO;
            wrapper.userInteractionEnabled = NO;
            [wrapper addSubview:label];
        }
        label.text = title;
        return wrapper;
    }
    
    - (void)addLabel:(NSString *)label ofSize:(CGFloat)pointSize toComponent:(NSInteger)component leftAlignedAt:(CGFloat)offset baselineAlignedWithFontOfSize:(CGFloat)titlePointSize {
        NSParameterAssert(component < [self numberOfComponents]);
    
        NSInteger tag = kMagicTag + component;
        UILabel *oldLabel = (UILabel *) [self viewWithTag:tag];
        if (oldLabel != nil && [oldLabel.text isEqualToString:label])
            return;
    
        NSInteger n = [self numberOfComponents];
        CGFloat total = 0.0;
        for (int c = 0; c < component; c++)
            offset += [self.delegate pickerView:self widthForComponent:c];
        for (int c = 0; c < n; c++)
            total += [self.delegate pickerView:self widthForComponent:c];
        offset += (self.bounds.size.width - total) / 2;
    
        offset += 2 * component; // internal UIPicker metrics, measured on a screenshot
        offset += 4; // add a gap
    
        CGFloat baselineHeight = [@"X" sizeWithFont:[UIFont boldSystemFontOfSize:titlePointSize]].height;
        CGFloat labelHeight = [@"X" sizeWithFont:[UIFont boldSystemFontOfSize:pointSize]].height;
    
        UILabel *labelView = [self shadedLabelWithText:label ofSize:pointSize];
        labelView.frame = CGRectMake(offset,
                                     (self.bounds.size.height - baselineHeight) / 2 + (baselineHeight - labelHeight) - 1,
                                     labelView.frame.size.width,
                                     labelView.frame.size.height);
        labelView.tag = tag;
    
        UIView *selectionBarView = nil;
        NSMutableArray *selectionBars = [NSMutableArray array];
        for (UIView *subview in self.subviews) {
            if ([[[subview class] description] isEqualToString:kSelectionBarClassName])
                [selectionBars addObject:subview];
        }
        if ([selectionBars count] == n) {
            [selectionBars sortUsingFunction:compareViews context:NULL];
            selectionBarView = [selectionBars objectAtIndex:component];
        }
        if (oldLabel != nil) {
            [UIView beginAnimations:nil context:oldLabel];
            [UIView setAnimationDuration:0.25];
            [UIView setAnimationDelegate:self];
            [UIView setAnimationDidStopSelector:@selector(YS_barLabelHideAnimationDidStop:finished:context:)];
            oldLabel.alpha = 0.0f;
            [UIView commitAnimations];
        }
        // if the selection bar hack stops working, degrade to using 60% alpha
        CGFloat normalAlpha = (selectionBarView == nil ? 0.6f : 1.0f);
        if (selectionBarView != nil)
            [self insertSubview:labelView aboveSubview:selectionBarView];
        else
            [self addSubview:labelView];
        if (oldLabel != nil) {
            labelView.alpha = 0.0f;
            [UIView beginAnimations:nil context:oldLabel];
            [UIView setAnimationDuration:0.25];
            [UIView setAnimationDelay:0.25];
            labelView.alpha = normalAlpha;
            [UIView commitAnimations];
        } else {
            labelView.alpha = normalAlpha;
        }
    }
    
    - (void)YS_barLabelHideAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(UIView *)oldLabel {
        [oldLabel removeFromSuperview];
    }
    
    @end
    

    - (void)updateFloorLabel {
        NSInteger floor = [self.pickerView numberOfRowsInComponent:0] - [self.pickerView selectedRowInComponent:0];
        NSString *suffix = @"th";
        if (((floor % 100) / 10) != 1) {
            switch (floor % 10) {
                case 1:  suffix = @"st"; break;
                case 2:  suffix = @"nd"; break;
                case 3:  suffix = @"rd"; break;
            }
        }
        [self.pickerView addLabel:[NSString stringWithFormat:@"%@ Floor", suffix]
                           ofSize:21
                      toComponent:0
                    leftAlignedAt:50
    baselineAlignedWithFontOfSize:25];    
    }
    
    - (void)viewDidLoad {
      ...
      [self.pickerView layoutSubviews];
      [self updateFloorLabel];
      ...
    }
    
    - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
        NSString *s = [NSString stringWithFormat:@"%d", [pickerView numberOfRowsInComponent:0] - row];
        return [pickerView viewForShadedLabelWithText:s ofSize:25 forComponent:0 rightAlignedAt:46 reusingView:view];
    }
    
    - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
        [self updateFloorLabel];
    }
    

    享受

        3
  •  7
  •   Kun Hu    10 年前

    假设我们想要实现一个用于选择距离的选择器视图,有两列,一列表示距离,一列表示单位,即公里。然后我们希望第二列是固定的。我们可以通过一些委托方法来实现。

    - (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
    {
        if (component == 0) {
            return self.distanceItems[row];
        }
        else {
            return @"km";
        }
    }
    
    -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
        return 2;
    }
    
    -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
        if (component == 0) {
            return [self.distanceItems count];
        }
        else {
        // when it comes to the second column, only one row.
            return 1;
        }
    }
    

    enter image description here

        4
  •  4
  •   keremk    16 年前

    您可以做两件事:

    UIPickerViewDelegate 方法:

    • - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

    • 并为实现中的选定行返回不同的文本 - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component

    如果需要使用文本以外的内容作为所选行的区分符,则基本上需要创建自己的行 CustomPickerView UIPickerView 然后

    • 首先实施 - (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated 并跟踪选定的行。

    • - (UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component 为所选行生成不同的视图。

    SDK中提供了使用UIPickerView或实现自定义UIPickerView的示例,称为UICatalog。

        5
  •  3
  •   Community clintgh    7 年前

    我收到了一个在iOS 7中运行良好的答案 question ,这是一个相当酷的游戏 trick .

    其思想是创建多个组件,对于这些标签组件,指定它是一行。对于某些人的浮雕外观,您可以使用委托方法返回NSAttributedString:

    - (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component

        6
  •  1
  •   Ed Marty    16 年前

    与其在UIPickerView中添加标签,不如将其粘贴在其顶部,作为重叠的同级标签。唯一有问题的是如何获得相同的字体。我不知道如何获得浮雕的外观,但也许其他人有,在这种情况下,这不是一个真正的问题。

        7
  •  1
  •   devguy    15 年前

    要重新创建标签的浮雕外观…只需使用文本创建一个图像,以便可以轻松地对文本应用非常相似的效果…然后使用UIImageView而不是标签

        8
  •  1
  •   Konrad G    11 年前
        9
  •  0
  •   mikechambers    15 年前

    您能说明在哪里定义pickerTop和pickerSize吗?

        CGFloat pickerTop = timePicker.bounds.origin.y;
    CGSize pickerSize = timePicker.bounds.size;
    

    这就是我所拥有的,但皮克托普似乎错了。

    迈克

        11
  •  0
  •   Dmitriy R    13 年前

        12
  •  0
  •   nh32rg    4 年前

    水平对齐多个选择器,使它们之间没有空间,并关闭静态列的用户交互。

    在每个选择器上设置一个标记,以确定哪个选择器是数据源调用中的pickerView。静态列的行数显然为1。