修复UIPickerView的select栏中的标签

在时钟应用程序中,计时器屏幕显示一个select器(在UIDatePickerModeCountDownTimer模式下可能是一个UIPicker ),在select栏中显示一些文本(在这种情况下为“小时”和“分钟”)。

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

有没有办法在标准UIPickerView组件的select栏中显示这样的固定标签?

我没有find任何API来帮助。 一个build议是添加一个UILabel作为select器的子视图,但没有奏效。


回答

我遵循埃德·马蒂的build议(下面的答案),它的工作原理! 不完美,但应该愚弄人。 作为参考,这里是我的实施,随时让它变得更好…

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

创build你的select器,创build一个带有阴影的标签,并将其推送到selectionIndicator视图下方的选取器子视图。

它看起来像这样

 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... 

– 付款

几年前,我把dizy的答案变成了UIPickerView的一个类别。 刚刚证实,它仍然适用于iOS SDK 4.3,并在这里发布。 它允许你添加一个标签(XX小时),并像UIDatePicker一样为这个标签添加animation变化(例如1小时 – > 3小时)。

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

请享用!

假设我们要实现一个select器视图来select距离,有2列,一个是距离,一个是单位,是km。 那么我们希望第二列是固定的。 我们可以通过一些委托方法来实现。

 - (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; } } 

现在我们有这个: 在这里输入图像描述

我想这是最简单的方法。

有两件事你可以做:

如果行中的每一行和组件都是一个简单的文本,那么您可以简单地使用默认的UIPickerView实现,并在您的控制器中实现以下UIPickerViewDelegate方法:

  • - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component跟踪选中的行

  • 并在你的实现- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component返回一个不同的文本

如果您需要使用除文本以外的其他内容作为所选行的CustomPickerView ,则基本上需要创build自己的从UIPickerView派生的CustomPickerView ,然后

  • 首先实现- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated并跟踪select哪一行。

  • 然后实现- (UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component来为选定的行生成不同的视图。

使用UIPickerView或实现自定义UIPickerView的示例在SDK中可用,名为UICatalog。

我收到了一个在iOS 7中对我的问题很好的回答,这是一个很酷的技巧 。

这个想法是创build多个组件,并为这些标签组件,指定它是一个单一的行。 对于某些人的浮雕效果,可以使用委托方法返回NSAttributedStrings:

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

而不是在UIPickerView中添加一个标签,只是把它粘在它的顶部,作为与之重叠的兄弟。 唯一有问题的是如何获得相同的字体。 我不知道如何得到浮雕的外观,但也许有人做,在这种情况下,这根本不是问题。

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

我也面临同样的问题。 你可以在GitHub上发布的自定义时间select器中看到工作示例:
https://github.com/kgadzinowski/iOSSecondsTimerPicker
它正是你想要的。

你可以显示你在哪里定义pickerTop和pickerSize?

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

这是我的,但pickerTop似乎是错的。

麦克风

浮雕在这里解释: 添加浮雕到导航项目。标题视图中的UILabel(如导航项目标题所示)

请注意,xib编辑器允许添加子视图,所以您可以避免在维上使用太多的编码和猜测。