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]; } } } @endO 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