2008-12-15 81 views
31

在時鐘應用程序中,定時器屏幕顯示一個選擇器(可能爲UIDatePickerModeCountDownTimer模式下的UIPicker),並在選擇欄中顯示一些文本(本例中爲「小時」和「分鐘」 。UIPickerView的選擇欄中的固定標籤

(編輯)請注意,這些標籤是固定:當拾取輪滾動時它們不移動。

有沒有辦法在標準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]; 
} 
+0

這是否適用於多個組件?我有3個組件,我試圖給每個組件添加一個標籤。我觀察到的是,所有3個標籤都作爲子視圖添加到第一個組件。如何找到插入標籤子視圖的視圖,以便標籤可以正確顯示? – 2010-10-27 14:59:53

+1

總之,訣竅是使用UIView,它嵌入PickerView和標籤。將標籤直接添加到iOS 5中的PickerView是不可能的。 – 2012-03-17 09:26:04

+0

最終結果plox的屏幕截圖? :) – FooBar 2014-11-13 13:59:39

回答

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

- 前支付它

+0

這是否適用於多個組件?我有3個組件,我試圖給每個組件添加一個標籤。我觀察到的是,所有3個標籤都作爲子視圖添加到第一個組件。如何找到插入標籤子視圖的視圖,以便標籤可以正確顯示? – 2010-10-27 15:13:37

+1

的優秀答案已經變成了UIPickerView的簡潔子類[這裏](http://blog.nottoobadsoftware.com/2009/03/a-uipickerview-with-labels/)。 – Josh 2011-06-29 11:09:52

4

有兩件事情可以做:

如果行的每一行和組件是一個簡單的文本,比你只使用默認UIPickerView執行情況是,在你的控制器實現以下UIPickerViewDelegate方法:

  • - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component跟蹤哪個被選擇行

  • ,並返回對於選擇的行不同的文字在你的實現- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component

如果你需要有比文本作爲區分對於選擇的行以外的東西,比你基本上需要創建自己的CustomPickerViewUIPickerView派生然後

  • 首先實現- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated並跟蹤其被選擇行。

  • 然後執行- (UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component爲所選行生成不同的視圖。

在SDK中提供了一個使用UIPickerView或實現自定義UIPickerView的示例,名爲UICatalog。

+1

除了一件事以外,還有一個不錯的技巧:按照我的要求,使用你的方法,標籤在Timer picker中的固定時間不像「小時」。 它仍然有點幫助我想,至少有一個暗示每個選擇器組件是指什麼。但只要你真的使它滾動,錯覺消失... – squelart 2008-12-16 02:42:09

+0

另外,UICatalog示例在標準或自定義的UIPickerView中沒有這些固定標籤...這可能意味着它可能不是由框架提供的。這看起來很愚蠢,因爲他們已經爲Timer選取器實現了它! – squelart 2008-12-17 23:58:20

1

與其在UIPickerView中添加標籤,只是將其放在頂部,作爲與其重疊的兄弟。唯一有問題的是如何獲得相同的字體。我不知道如何得到浮雕的外觀,但也許別人會這樣做,在這種情況下,這根本不是問題。

0

你可以顯示你在哪裏定義pickerTop和pickerSize嗎?

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

這就是我的,但pickerTop似乎是錯的。

邁克

+0

代碼不適合評論,所以我已經將它放在上面的答案中。請注意魔法(40),這可能需要改變,具體取決於您是使用導航欄還是其他需要屏幕空間的事物。如果有人知道更好的方法,請告訴我! – squelart 2009-02-14 22:18:02

1

要重新創建標籤上的浮雕看看...剛剛創建的圖像與文本,讓你可以很容易地應用效果非常相似的文字...然後用,而不是標籤

0

注意THA t xib編輯器允許添加子視圖,因此您可以避免在維上使用過多的編碼和猜測。

11

幾年前,我已將dizy的答案轉換爲UIPickerView的類別。剛剛證實,它仍然適用於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 <[email protected]> 
// 
// 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 <[email protected]> 
// 
// 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

我收到了一個適用於iOS 7的回答,我的question,這是一個很酷的trick

構思是創建多個組件,併爲這些標籤組件指定它是單個行。對於浮雕的外觀,有些人,你可以委託方法返回NSAttributedStrings:

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

7

比方說,我們要實現選擇距離的選擇器視圖中,有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; 
    } 
} 

現在有這樣的: enter image description here

我想這是最簡單的方法。