iphone - stepper - Etiquetas fijas en la barra de selección de un UIPickerView
switch ios size (11)
En la aplicación de relojes, la pantalla del temporizador muestra un selector (probablemente un UIPicker
en modo UIDatePickerModeCountDownTimer
) con texto en la barra de selección ("horas" y "minutos" en este caso).
(edit) Tenga en cuenta que estas etiquetas son fijas : no se mueven cuando la rueda del selector está rodando.
¿Hay alguna manera de mostrar esas etiquetas fijas en la barra de selección de un componente UIPickerView
estándar?
No encontré ninguna API que ayudara con eso. Una sugerencia fue agregar un UILabel
como una subvista del selector, pero eso no funcionó.
Responder
Seguí el consejo de Ed Marty (respuesta a continuación), ¡y funciona! No es perfecto, pero debería engañar a la gente. Como referencia, aquí está mi implementación, siéntete libre de hacerlo mejor ...
- (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];
}
¿Puedes mostrar dónde defines pickerTop y pickerSize?
CGFloat pickerTop = timePicker.bounds.origin.y;
CGSize pickerSize = timePicker.bounds.size;
Eso es lo que tengo, pero PickerTop parece estar mal.
micro
Cree su seleccionador, cree una etiqueta con una sombra y empújela a la vista inferior de un seleccionador debajo de la vista del seleccionador de selección.
Se vería algo como esto
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...
-- Pagalo despues
Digamos que queremos implementar una vista del seleccionador para seleccionar la distancia, hay 2 columnas, una para la distancia, una para la unidad, que es km. Entonces queremos que la segunda columna sea arreglada. Podemos hacerlo a través de algunos métodos delegados.
- (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;
}
}
Ahora tenemos esto:
Supongo que esta es la forma más simple.
El relieve se explica aquí: Agregar Relieve a un UILabel en un navigationItem.titleView (como se ve con navigationItem.title)
En lugar de agregar una etiqueta dentro de UIPickerView, simplemente colóquela sobre ella, como un hermano que se superpone. Lo único que es problemático es cómo obtener la misma fuente. No sé cómo obtener ese aspecto realzado, pero tal vez alguien más lo haga, en cuyo caso, no es realmente un problema en absoluto.
Hay 2 cosas que puedes hacer:
Si cada fila y componente en fila es un texto simple, entonces simplemente puede usar la implementación predeterminada de UIPickerView tal como está, y en su controlador implementar los siguientes métodos UIPickerViewDelegate
:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
para realizar un seguimiento de qué fila se seleccionay devuelve un texto diferente para la fila seleccionada en su implementación de
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
Si necesita tener algo más que texto como diferenciador para la fila seleccionada, entonces básicamente necesita crear su propio CustomPickerView
que se deriva de UIPickerView
y luego
Primero implemente el
- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated
y realice un seguimiento de la fila seleccionada.Luego implemente el
- (UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component
para generar una vista diferente para la fila seleccionada.
Un ejemplo para usar UIPickerView o implementar UIPickerView personalizado está disponible en el SDK, llamado UICatalog.
He convertido la respuesta de Dizy en una categoría en UIPickerView
un par de años. Acabo de verificar que todavía funciona con iOS SDK 4.3 y publicarlo aquí. Le permite agregar una etiqueta (XX horas) y animar los cambios a esta etiqueta (por ejemplo, 1 hora -> 3 horas) como UIDatePicker
hace 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
Y la implementación:
// 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
Ejemplo de uso (en un controlador de vista):
- (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];
}
¡Disfrutar!
Para volver a crear el aspecto en relieve de las etiquetas ... solo cree una imagen con el Texto, para que pueda aplicar fácilmente un efecto muy similar al del texto ... y luego use UIImageViews en lugar de Etiquetas
Recibí una respuesta que funciona bien en iOS 7 con mi question , que es un trick genial.
La idea es crear múltiples componentes, y para esos componentes de etiquetas, especifique que es una sola fila. Para el aspecto de relieve que algunas personas tienen, puede devolver NSAttributedStrings con el método de delegado:
- (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component
También me enfrenté al mismo problema. Puede ver el ejemplo de trabajo en mi selector de tiempo personalizado publicado en GitHub:
https://github.com/kgadzinowski/iOSSecondsTimerPicker
Hace exactamente lo que quieres.
Tenga en cuenta que el editor xib también permite agregar vistas secundarias para que pueda evitar el uso de demasiada codificación y adivinar las dimensiones.