Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UILabel doesn't show inputView

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
like image 685
massyc Avatar asked Oct 11 '12 21:10

massyc


5 Answers

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()
    }

}
like image 193
Dmitriy Kirakosyan Avatar answered Nov 14 '22 06:11

Dmitriy Kirakosyan


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];
}
like image 5
ims Avatar answered Nov 14 '22 07:11

ims


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];
}
like image 4
rdelmar Avatar answered Nov 14 '22 06:11

rdelmar


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
like image 3
massyc Avatar answered Nov 14 '22 05:11

massyc


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()
    }

}
like image 3
Blazej SLEBODA Avatar answered Nov 14 '22 07:11

Blazej SLEBODA