I'm trying to make a UITextView that detects links AND becomes editable when a tap doesn't fall on a link.
The native iphone notes app exhibits this behavior, as does simplenote.
The closest solution I have found is outlined here: http://blog.stevex.net/2012/05/editable-uitextview-with-links/ Using this code:
// Add the tap gesture recogniser to the view.
- (void)configureTextView {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textViewTapped:)];
textView.editable = NO;
textView.dataDetectorTypes = UIDataDetectorTypeLink;
[textView addGestureRecognizer:recognizer];
}
// Notification from the recogniser that the UITextView was tapped
- (void)textViewTapped:(UIGestureRecognizer *)recognizer {
UITextView *textView = (UITextView *)recognizer.view;
textView.editable = YES;
[textView becomeFirstResponder];
}
// UITextViewDelegate method
- (void)textViewDidEndEditing:(UITextView *)textView {
textView.editable = NO;
}
The problem with this solution is that it requires a long press to follow a link.
Related
I have a UITextView that is created programmatically on viewDidLoad using the method below. Keyboard is shown successfully right after [_answerTextView becomeFirstResponder] is called. And hid if the user enters return(\n) character.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if([text isEqualToString:#"\n"]) {
[self endEditing:YES];
return NO;
}
return YES;
}
After the keyboard is hid by the code above, I'm not able to bring it back anymore by tapping the UITextView again.
I tried showing the keyboard by calling [_answerTextView becomeFirstResponder] on Tap event for the parent UIView and it worked. But that's not what I want.
UITextView is created by the method below;
-(UITextView*)createSurveyTextView{
_answerTextView = [[UITextView alloc]initWithFrame:CGRectMake(0, 0, _optionsStackView.frame.size.width, 200)];
_answerTextView.keyboardType = UIKeyboardTypeDefault;
_answerTextView.text = #"";
_answerTextView.editable = true;
_answerTextView.selectable = true;
_answerTextView.delegate = self;
[_answerTextView becomeFirstResponder];
_answerTextView.backgroundColor = [UIColor whiteColor];
_answerTextView.textColor = [UIColor blackColor];
_answerTextView.layer.cornerRadius = 5;
[_optionsStackView addSubview:_answerTextView];
_answerTextView.font = [UIFont systemFontOfSize:20];
[_answerTextView setReturnKeyType:UIReturnKeyDone];
[_optionsStackView bringSubviewToFront:_answerTextView];
[self bringSubviewToFront:_answerTextView];
[_answerTextView setUserInteractionEnabled:true];
UITapGestureRecognizer *mainViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTextViewTap:)];
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap2:)];
[_answerTextView addGestureRecognizer:mainViewTap];
[self addGestureRecognizer:tap2];
return _answerTextView;
}
I want the UITextView to simply show the keyboard when the user taps on it self. What am I missing?
Edit: I have the same sources in another view, but everything works fine there. The only difference is, here the UITextView is created programmatically.
Edit 2: I got it worked. The _optionsStackView that the UITextView is added as a child is a UIStackView. I tried adding the _answerTextView to the parent UIView and everything works fine now. What would be the problem with the UIStackView?
Your problem very strange in your code.You can explantion why use UITapGestureRecognizer to this UITextView control.How do you think this lead to conflict?
I find your problem where in start,but I create new project to ensure.So I confirm that UITextView already has target with itself.Also you can't to add UITapGestureRecognizer.
What you're doing is almost correct, but just try changing endEditing: to [textView resignFirstResponder]:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:#"\n"]) {
[textView resignFirstResponder];
return NO;
}
return YES;
}
If that doesn't work, then it could be that one of your gestures is preventing the UITextView's own UITapGestureRecognizer from receiving the tap. Try disabling (or comment out the creation of) both of your recognizers to verify that it is the issue. If it is, then you can fix it using the UIGestureRecognizerDelegate methods.
You did not use common way to hide/close the keyboard.
Try this common way:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
return [textField resignFirstResponder];
}
This is a method in the UITextFieldDelegate, use it and remove your code:
if([text isEqualToString:#"\n"]) {
[self endEditing:YES];
return NO;
}
Edit:
#siburb You are right. My bad.
UITapGestureRecognizer maybe the root cause.
Edit:
#Semih Akbas
Repalce:
[_optionsStackView addSubview:_answerTextView];
To:
[_optionsStackView addArrangedSubview:_answerTextView];
I have searched a lot for how to detect link in UITextView with editable property set to true, but didn't find any solution. All solutions suggest to set editable to NO, but by requirement I can't set editable to NO.
Unfortunately you cannot have an editable UITextView with clickable links.
But you can try this code it might work. I get this from one tutorial: http://www.ama-dev.com/editable-uitextview-with-link-detection/
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(editTextRecognizerTabbed:)];
recognizer.delegate = self;
recognizer.numberOfTapsRequired = 1;
[self.textViewNotes addGestureRecognizer:recognizer];
- (void) editTextRecognizerTabbed:(UITapGestureRecognizer *) aRecognizer;
{
self.textViewNotes.dataDetectorTypes = UIDataDetectorTypeNone;
self.textViewNotes.editable = YES;
[self.textViewNotes becomeFirstResponder];
}
- (void)textViewDidEndEditing:(UITextView *)textView;
{
self.textViewNotes.editable = NO;
self.textViewNotes.dataDetectorTypes = UIDataDetectorTypeAll;
}
I've found this blog. From the example: to make a UITextView editable with clickable links you should setup a gestureRecognizer and make the UITextView editable on tap. The code is:
- (void)configureTextView {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textViewTapped:)];
recognizer.numberOfTapsRequired = 1;
textView.editable = NO;
textView.dataDetectorTypes = UIDataDetectorTypeLink;
[textView addGestureRecognizer:recognizer];
}
// Notification from the recogniser that the UITextView was tapped
- (void)textViewTapped:(UIGestureRecognizer *)recognizer {
UITextView *textView = (UITextView *)recognizer.view;
textView.editable = YES;
[textView becomeFirstResponder];
}
// UITextViewDelegate method
- (void)textViewDidEndEditing:(UITextView *)textView {
textView.editable = NO;
}
You can use UITapGestureRecognizer for that UITextField .
I have a UIView with a UITextField, UIButton and UITable view. The textfield and button compromise a search bar and the results are then loaded into the table view.
I'd like to make it so they keyboard dismisses. If the user taps something when they are editing the text field. My strategy would be to add a gesture recognizer to the UIView, but then gesture recognizer seems to intercept all the touches from the table view and you |tableView:didSelectCellAtIndexPath:| never gets called. Whats interesting (to me at least) is that the UIButton is still tap-able when the user is editing the field even though the UITableView isn't.
I've tried implementing |gestureRecognizer::shouldRecognizeSimultaneouslyWithGestureRecognizer:| to alway return yes, but that doesn't help. I've also tried setting
singleTapRecognizer.cancelsTouchesInView = NO;
which also doesn't help.
I'm happy with the idea of adding and removing the gesture recognizer when the text field calls |textFieldDidBeginEditing:| and |textFieldDidFinishEditing:|, though this feels messy and it still takes two taps to touch a cell when you're editing the text field (one to dismiss they keyboard and remove the recognizer, and one to tap the cell).
Is there a better way?
Relevant code below:
- (void)loadView {
[super loadView];
self.scrollView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.scrollView.backgroundColor = [FDEColors viewBackgroundColor];
self.view = self.scrollView;
self.searchField = [[UITextField alloc] initWithFrame:CGRectZero];
self.searchField.placeholder = #"What are you looking for?";
self.searchField.backgroundColor = [FDEColors textFieldBackgroundColor];
self.searchField.clipsToBounds = YES;
self.searchField.layer.borderColor = [[FDEColors buttonColor] CGColor];
self.searchField.layer.borderWidth = 1.f;
self.searchField.returnKeyType = UIReturnKeySearch;
self.searchField.delegate = self;
[self.view addSubview:self.searchField];
self.searchButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.searchButton.backgroundColor = [FDEColors buttonColor];
[self.searchButton setTitle:#"Search" forState:UIControlStateNormal];
[self.searchButton addTarget:self
action:#selector(searchPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.searchButton];
self.resultsTableView =
[[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
self.resultsTableView.delegate = self;
self.resultsTableView.dataSource = self;
self.resultsTableView.backgroundColor = [FDEColors viewBackgroundColor];
[self.resultsTableView setSeparatorInset:UIEdgeInsetsZero];
self.resultsTableView.layoutMargins = UIEdgeInsetsZero;
[self.resultsTableView registerClass:[FDESearchResultsCell class]
forCellReuseIdentifier:[FDESearchResultsCell reuseIdentifier]];
[self.view addSubview:self.resultsTableView];
self.singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:self.singleTapRecognizer];
}
- (void)dismissKeyboard {
[[self view] endEditing:YES];
}
Try implementing the gesture recognizer's delegate method:
- (BOOL) gestureRecognizer:ShouldReceiveRouch:
In this method, check for the touch's location. If it's inside the tableview, return no, so the tableview can receive the touch. Otherwise, return YES and let the recognizer handle the touch.
Edit: As for the button receiving the touch despite the recognizer's existence, as of iOS6 Apple decided to give buttons and some other controls priority when it comes to recognizing gestures. It only applies to antagonizing gestures though, in your case a single tap. If for example you also had a pan recognizer, the recognizer would have precedence, not the button.
Edit 2:
An example implementation of the method mentioned above:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Determine if the touch is inside the custom subview
if ([touch view] == self.yourTableView){
// If it is, prevent all of the delegate's gesture recognizers
// from receiving the touch
return NO;
}
return YES;
}
I have a UITextView which becomes first responder by tapping. The code looks like this.
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(editTextRecognizerTabbed:)];
recognizer.delegate = self;
recognizer.numberOfTapsRequired = 1;
[self.textViewNotes addGestureRecognizer:recognizer];
- (void) editTextRecognizerTabbed:(UITapGestureRecognizer *) aRecognizer;
{
self.textViewNotes.dataDetectorTypes = UIDataDetectorTypeNone;
self.textViewNotes.editable = YES;
[self.textViewNotes becomeFirstResponder];
}
So when the user taps on the textview, the textview becomes the first-responder and the focus is on the end of the text. I want place the focus on the tapped position in the Text.
Any idea?
I want to show UITableView on tapping of UITextView.
key board should not be shown.
the problem is on tapping UITextView both UITableView and keyboard are shown.
I want only table view to be shown. I want to display data got from web service in UITextView which is not editable.on pressing edit button in navigation bar that time on tapping UITextView ,UITableView should be popped up and not keyboard.
My code is below:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
if([text isEqualToString:#"\n"]) {
[textView resignFirstResponder];
return NO;
}
return YES;
}
-(void)textViewDidBeginEditing:(UITextView *)textView{
[self.view endEditing:YES];
tableVw=[[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 500)];
tableVw.dataSource=self;
tableVw.delegate=self;
[self.view addSubview:tableVw];
}
-(void)viewDidLoad{
[super viewDidLoad];
txtVwDescription=[[UITextView alloc]initWithFrame:CGRectMake(10, 270, 300,stringSize.height+10)];
txtVwDescription.backgroundColor=[UIColor clearColor];
txtVwDescription.layer.cornerRadius=5;
txtVwDescription.backgroundColor=[UIColor colorWithRed:0.662745 green:0.662745 blue:0.662745 alpha:0.3];
[txtVwDescription setFont:[UIFont systemFontOfSize:13.0]];
txtVwDescription.contentInset=UIEdgeInsetsMake(0, 0, 0, 0);
[txtVwDescription setUserInteractionEnabled:NO];
txtVwDescription.editable=NO;
txtVwDescription.delegate=self;
[self.view addSubview:txtVwDescription];
}
-(void)edit{
[txtVwDescription setUserInteractionEnabled:YES];
txtVwDescription.editable=NO;
[txtVwDescription resignFirstResponder];
}
you can use UITextViewDelegate's textViewShouldBeginEditing method returning NO, and presenting there the UITableView. In such a way the keyboard should not be presented.
Rather than having to mess with textViewDelegate methods, I find it easier to simply disable editing for the textView and add a gesture recognizer to it like this:
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(showTableView)];
UITextView *textView = [[UITextView alloc] initWithFrame:....];
textView.editable = NO;
[textView addGestureRecognizer:tapGestureRecognizer];
This way, no keyboard is shown and you don't have to mess around with navigation inside the delegate methods. All you need to do now is handle the tap with the method you provided the recognizer:
- (void)showTableView {
// do stuff here
}
EDIT:
You might want to also consider using a UILabel instead of a UITextView if you are not planning on making it editable at all. You can implement the same gesture recognizer code as well, just add it to the label instead of the textView.
UITextField and UITextView classes has a property named inputView for displaying custom views instead of the default keyboards. The system shows whatever UIView descendant class you set to that property when the textfield/view becomes first responder. So simply add,
UITextView *textView = [[UITextView alloc] initWithFrame:someFrame];
textView.inputView = self.myTableView; //this is your already initialised tableview