在时钟应用程序,计时器屏幕显示了一个选择器(可能是一个UIPicker
在UIDatePickerModeCountDownTimer
模式),在选择栏一些文本("小时",并在这种情况下,"分钟").
(编辑)请注意,这些标签是固定的:当拣选轮滚动时,它们不会移动.
有没有办法在标准UIPickerView
组件的选择栏中显示这样的固定标签?
我没有找到任何有助于此的API.建议是添加一个UILabel
作为选择器的子视图,但这不起作用.
回答
我按照Ed Marty的建议(下面回答),它的确有效!不完美,但它应该骗人.作为参考,这是我的实现,随意让它变得更好......
- (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]; }
dizy.. 13
创建选择器,创建带阴影的标签,并将其推送到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...
- 支付它
创建选择器,创建带阴影的标签,并将其推送到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...
- 支付它
UIPickerView
几年前我把dizy的答案变成了一个类别.刚刚验证它仍适用于iOS SDK 4.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// // 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 // 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// // 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]; }
请享用!
假设我们想要实现一个选择距离的选择器视图,有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; } }
现在我们有了这个:
我想这是最简单的方法.