Исправлены ярлыки на панели выбора UIPickerView
В приложении часов на экране таймера отображается сборщик (возможно, 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];
}
Ответы
Ответ 1
Создайте свой сборщик, создайте ярлык с тенью и подтащите его под подвью подбора под представлением 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...
- оплатить его вперед
Ответ 2
Я превратил 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 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 "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 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
Предположим, что мы хотим реализовать представление выбора для выбора расстояния, есть 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]()
Я думаю, это самый простой способ.
Ответ 4
Есть две вещи, которые вы можете сделать:
Если каждая строка и компонент в строке является простым текстом, вы можете просто использовать реализацию по умолчанию UIPickerView как есть, а в вашем контроллере реализовать следующие методы 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
для создания другого представления для выбранной строки.
Образец для использования UIPickerView или реализации пользовательского UIPickerView доступен в SDK, называемом UICatalog.
Ответ 5
Я получил ответ, который хорошо работает в iOS 7 для моего question, что довольно круто трюк.
Идея состоит в том, чтобы создать несколько компонентов, а для этих компонентов ярлыков указать, что это одна строка. Для тисненый взгляд, который есть у некоторых людей, вы можете вернуть NSAttributedStrings с помощью метода делегирования:
- (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component
Ответ 6
Вместо того, чтобы добавлять ярлык в UIPickerView, просто вставьте его поверх него в качестве брата, который перекрывает его. Единственное, что проблематично, - это получить один и тот же шрифт. Я не знаю, как получить этот рельефный вид, но, возможно, кто-то другой, и в этом случае это не проблема вообще.
Ответ 7
Чтобы повторно создать тисненый поиск ярлыков... просто создайте образ с текстом, чтобы вы могли легко применить очень похожий эффект к тексту... а затем использовать UIImageView вместо ярлыков
Ответ 8
У меня также была такая же проблема. Вы можете увидеть рабочий пример в моем настраиваемом тайм-листе, опубликованном в GitHub:
https://github.com/kgadzinowski/iOSSecondsTimerPicker
Он делает именно то, что вы хотите.
Ответ 9
Можете ли вы показать, где вы определяете pickerTop и pickerSize?
CGFloat pickerTop = timePicker.bounds.origin.y;
CGSize pickerSize = timePicker.bounds.size;
Это то, что у меня есть, но pickerTop кажется неправильным.
микрофон
Ответ 10
Здесь описывается тиснение: Добавление Emboss в UILabel в файле navigationItem.titleView(как видно с помощью navigationItem.title)
Ответ 11
Обратите внимание, что редактор xib также позволяет добавлять дочерние представления, поэтому вы можете избежать слишком большого количества кодирования и гадания по размерам.