Hoje pela manhã estava fazendo uma telinha no iOS que tinha 2 campos de data. E como o iOS tem um componente padrão para datas, o UIDatePicker, resolvi usá-lo. Entretanto ele ocupa muito espaço e fica inviável colocar dois na tela. Foi aí que me surgiu a ideia de utilizar uma UIActionSheet personalizada. Onde eu poderia selecionar a data, e em seguida a ActionSheet desapareceria da tela.
Para exemplificar isso, crie um novo projeto no Xcode, se tiver dúvidas, dê uma olhada aqui. Na tela da sua aplicação, arraste dois Labels e dois Buttons e deixe-os conforme abaixo. Nos botões, mudei a propriedade Type para Detail Disclosure e a propriedade Tag para 1 e 2 respectivamente.
Crie os Outlets para os dois Labels e utilize o método changeDateClick no Touch Up Inside dos dois botões. Nós saberemos qual botão foi clicado através da propriedade Tag.
#import <UIKit/UIKit.h> @interface NGDetailViewController : UIViewController <UIActionSheetDelegate> - (IBAction)changeDateClick:(id)sender; @property (weak, nonatomic) IBOutlet UILabel *txtData1; @property (weak, nonatomic) IBOutlet UILabel *txtData2; @endNote que nossa classe implementa o protocolo UIActionSheetDelegate, necessário para sabermos qual botão o usuário clicou na ActionSheet. Deixe a implementação da classe conforme abaixo:
#import "NGDetailViewController.h"
@implementation NGDetailViewController
@synthesize txtData1;
@synthesize txtData2;
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:
nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidUnload {
[self setTxtData1:nil];
[self setTxtData2:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation !=
UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)changeDateClick:(id)sender {
UIButton *button = sender;
UIDeviceOrientation orientation =
[[UIDevice currentDevice] orientation];
int topMargin =
(orientation == UIDeviceOrientationPortrait) ?
40 : 60;
UIActionSheet *dateActionSheet =
[[UIActionSheet alloc] initWithTitle:
@"Selecione a data"
delegate:self
cancelButtonTitle:@"Cancelar"
destructiveButtonTitle:nil
otherButtonTitles:@"OK", nil];
[dateActionSheet setTag:button.tag];
[dateActionSheet setActionSheetStyle:
UIActionSheetStyleBlackTranslucent];
[dateActionSheet showInView:self.view];
[dateActionSheet setFrame:CGRectMake(
0, topMargin, self.view.frame.size.width, 400)];
}
- (void)willPresentActionSheet:
(UIActionSheet *)actionSheet {
// Inicializa o picker e adiciona ao actionSheet
UIDatePicker *pickerView =
[[UIDatePicker alloc] init];
[pickerView setDatePickerMode:UIDatePickerModeDate];
[actionSheet insertSubview:pickerView atIndex:1];
// A actionSheet tem 3 subviews agora,
// o picker e os dois botões
UIView *titleView =
[actionSheet.subviews objectAtIndex:0];
UIButton *btnOk =
[actionSheet.subviews objectAtIndex:2];
UIButton *btnCancel =
[actionSheet.subviews objectAtIndex:3];
UIDeviceOrientation orientation =
[[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait) {
// Mudou o Y do picker para ficar abaixo do título
pickerView.frame = CGRectMake(
pickerView.frame.origin.x,
titleView.frame.size.height + 20,
pickerView.frame.size.width,
pickerView.frame.size.height);
// Aqui também... só o Y, ficando abaixo do picker
btnOk.frame = CGRectMake(
btnOk.frame.origin.x,
pickerView.frame.origin.y +
pickerView.frame.size.height + 10,
btnOk.frame.size.width,
btnOk.frame.size.height);
// De novo só o Y, ficando abaixo do btnOk
btnCancel.frame = CGRectMake(
btnCancel.frame.origin.x,
btnOk.frame.origin.y +
btnOk.frame.size.height + 10,
btnCancel.frame.size.width,
btnCancel.frame.size.height);
} else {
// Aqui dei um espaço de 10 em X,
// em Y ficou abaixo do título 20px,
// na largura, o picker ocupa 2/3 da tela,
// e altura não mudou
pickerView.frame = CGRectMake(
10,
titleView.frame.size.height + 20,
((pickerView.frame.size.width) / 3 ) * 2,
pickerView.frame.size.height);
// No eixo X, o botão fica a direita do picker,
// em Y, fica alinhado com o topo do picker,
// a largura do botão é área entre o fim do
// picker e a margem direita da tela,
// a altura não mudou
btnOk.frame = CGRectMake(
pickerView.frame.origin.x +
pickerView.frame.size.width + 10,
pickerView.frame.origin.y,
(actionSheet.frame.size.width -
pickerView.frame.size.width -
pickerView.frame.origin.x - 20),
btnOk.frame.size.height);
// Aqui é similar ao anterior, a diferença é
// que em Y, o botão está abaixo do btnOk.
btnCancel.frame = CGRectMake(
pickerView.frame.origin.x +
pickerView.frame.size.width + 10,
btnOk.frame.origin.y +
btnOk.frame.size.height + 10,
(actionSheet.frame.size.width -
pickerView.frame.size.width -
pickerView.frame.origin.x - 20),
btnCancel.frame.size.height);
}
}
- (void)actionSheet:(UIActionSheet *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0){
UIDatePicker *picker =
[actionSheet.subviews objectAtIndex:1];
NSString *dataStr = [NSDateFormatter
localizedStringFromDate:picker.date
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterNoStyle];
if (actionSheet.tag == 1){
txtData1.text = [NSString
stringWithFormat:@"Data: %@", dataStr];
} else {
txtData2.text = [NSString
stringWithFormat:@"Data: %@", dataStr];
}
}
}
@end
O método shouldAutorotateToInterfaceOrientation define que nossa aplicação apenas não suporta a orientação de Portrait invertido (ou seja, o aparelho de cabeça pra baixo).A implementação do método changeDateClick inicia pegando a referência do botão que foi clicado. Em seguida pegamos a orientação do aparelho para definir a distância da ActionSheet para margem superior do aparelho. Depois criamos a ActionSheet passando as informações necessárias: título, botões, e o mais importante, o delegate (ou listener) que será chamado quando clicar em algum dos botões. Como nossa classe está implementando esse protocolo, passamos a instância da prórpria classe (self).
Na linha seguinte, setamos a tag da actionSheet para podermos identificar qual dos botões foi clicado para alterar a caixa de texto correspondente. Por fim, alteramos a área (frame) da actionSheet.
O método willPresentActionSheet irá personalizar a actionSheet, adicionando o UIDateTimePicker e reposicionando os botões de acordo com a orientação do aparelho. O código deste método está comentado. Então não vou entrar em detalhes.
O último método, assim como o anterior, é definido no protocolo UIActionSheetDelegate, ele vai ver se clicamos no primeiro botão (OK) e em caso positivo, pega a referência do UIDatePicker e obtém a data selecionada no componente. Em seguida, converte a data em uma NSString usando a classe NSDateFormatter. Feito isso, verifica a tag da actionSheet e altera o Label correspondente.
Se você executar a aplicação e clicar em um dos botões, a actionSheet é exibida abaixo.
Aproveite e teste a aplicação também em landscape.
É isso pessoal. Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber



Um comentário:
Show de bola, no maior estilo MacGyver! :D
Postar um comentário