I would use a UILabel to allow users to select a date with UIDatePicker.
To do this, I created an UILabel subclass overwriting the inputView and the inputAccessoryView properties making them writable; I also implemented the -(BOOL) canBecomeFirstResponder and the -(BOOL) isUserInteractionEnabled methods returning YES for both. Then I assigned an instance of UIDatePIcker to the inputView property.
At this point my expectation is that when the label is tapped an UIDatePicker should appear, but nothing happens.
Any help?
This is the code:
YPInteractiveUILabel.h
@interface YPInteractiveUILabel : UILabel
@property (readwrite) UIView *inputView;
@property (readwrite) UIView *inputAccessoryView;
- (BOOL) canBecomeFirstResponder;
- (BOOL) isUserInteractionEnabled;
@end
YPInteractiveUILabel.h
#import "YPInteractiveUILabel.h"
@implementation YPInteractiveUILabel
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
[self setInputView:datePicker];
}
return self;
}
- (BOOL)isUserInteractionEnabled
{
return YES;
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
@end
UILabel + UIDatePicker -- Swift version with Done button.
import UIKit
class DatePickerLabel: UILabel {
private let _inputView: UIView? = {
let picker = UIDatePicker()
return picker
}()
private let _inputAccessoryToolbar: UIToolbar = {
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.Default
toolBar.translucent = true
toolBar.sizeToFit()
return toolBar
}()
override var inputView: UIView? {
return _inputView
}
override var inputAccessoryView: UIView? {
return _inputAccessoryToolbar
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(doneClick))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
_inputAccessoryToolbar.setItems([ spaceButton, doneButton], animated: false)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(launchPicker))
self.addGestureRecognizer(tapRecognizer)
}
override func canBecomeFirstResponder() -> Bool {
return true
}
@objc private func launchPicker() {
becomeFirstResponder()
}
@objc private func doneClick() {
resignFirstResponder()
}
}
You can just ovveride inputView getter method, like explained in Apple documentation:
- (UIView *)inputView {
return myInputView;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
Then add a gesture or a button to call becomeFirstResponder:
- (void)showInputView:(id)sender {
[self becomeFirstResponder];
}
How about something like this. Rather than subclass the label, just add a gesture recognizer to it, and bring up the picker in the tap recognizer's handler. In the picker's action method, populate the label and dismiss the picker. This example works, but you'd probably want to add some animation to make it look better:
- (void)viewDidLoad {
[super viewDidLoad];
self.label.userInteractionEnabled = YES;
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(launchPicker:)];
[self.label addGestureRecognizer:tapper];
}
-(void)launchPicker:(UITapGestureRecognizer *) tapper {
UIDatePicker *picker = [[UIDatePicker alloc] initWithFrame:CGRectMake(5, 150, 300, 200)];
[picker addTarget:self action:@selector(updateLabel:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:picker];
}
-(IBAction)updateLabel:(UIDatePicker *)sender {
self.label.text = [NSString stringWithFormat:@"%@",sender.date ];
[sender removeFromSuperview];
}
Thanks to the suggestions (especially the comment from NeverBe and the answer proposed by rdelmar) I found the problem in my code. In brief, in order to show the input label, a call to the becomeFirstResponder method when the user tap the label is needed.
Following the UILabel subclass implementation corrected (the header file remains the same):
@implementation YPInteractiveUILabel
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
[self setInputView:datePicker];
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(launchPicker:)];
[self addGestureRecognizer:tapper];
}
return self;
}
- (BOOL)isUserInteractionEnabled
{
return YES;
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)launchPicker:(UITapGestureRecognizer *) tapper
{
[self becomeFirstResponder];
}
@end
This is a code snipet of @dmitriy-kirakosyan updated to Swift 5
class DatePickerLabel: UILabel {
private let _inputView: UIView? = {
let picker = UIDatePicker()
return picker
}()
private let _inputAccessoryToolbar: UIToolbar = {
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.sizeToFit()
return toolBar
}()
override var inputView: UIView? {
return _inputView
}
override var inputAccessoryView: UIView? {
return _inputAccessoryToolbar
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.plain, target: self, action: #selector(doneClick))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
_inputAccessoryToolbar.setItems([ spaceButton, doneButton], animated: false)
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(launchPicker))
self.addGestureRecognizer(tapRecognizer)
}
override var canBecomeFirstResponder: Bool {
return true
}
@objc private func launchPicker() {
becomeFirstResponder()
}
@objc private func doneClick() {
resignFirstResponder()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With