Can I Programmatically Select Text in UITextView? - iphone

I want to select Text on UITextView, similar to the default "Select" and "Select All" pop options we see when we tap. I want to the user the ability to do that from my custom menu. I played with selectedRange but that doesnt seem to do the trick. Any ideas?
Thanks

The selectedRange property should do it but, as mentioned in the documentation, only in iPhone OS 3.0 and later. In 2.2 and earlier, the selectedRange property is actually an insertion point.

As mentioned in the accepted answer, the selectedRange property is the thing you need, but beware that if you are using the -textViewDidBeginEditing: delegate method you might need to defer one run loop in order to win out over the user generated "insertion" action:
- (void)textViewDidBeginEditing:(UITextView *)textView
{
// Look for the default message and highlight it if present
NSRange defaultMsgRange = [textView.text rangeOfString:NSLocalizedString(#"TEXTFIELD_DEFAULT_MESSAGE", nil) options:NSAnchoredSearch];
BOOL isDefaultMsg = !(defaultMsgRange.location == NSNotFound && defaultMsgRange.length == 0);
if (isDefaultMsg) {
// Need to delay this by one run loop otherwise the insertion wins
[self performBlock:^(id sender) { // (BlocksKit - use GCD otherwise)
textView.selectedRange = defaultMsgRange;
} afterDelay:0.0];
}
}

Related

How can I disable/enable UISearchBar keyboard's Search button?

I am using UISearchBar in my code. I have imported its delegate in header file and implemented some delegate methods in implementation file also.
When we tap on the UISearchBar, a keyboard will appear to enter text. The return key of the keyboard is "Search" button. It will disabled by default. When we enter a character, It will get enabled. (Am I right?)
Here the problem comes.. I want to enable the UISearchBar keyboard's return key when the user types atleast two letters.
Is it possible? If yes, how can we do it?
Thanks
You can't disable the search button. What you can do is use the UISearchBarDelegate methods to figure out if you should take action on the search button being clicked, like so:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
if (searchBar.text.length < 2) {
return;
}
else {
// Do search stuff here
}
}
The Apple Documentation for this is very useful as well, and is a great starting point for customizing the searchBar's behavior.
Short answer is no...
Longer, hackier and more exotic one is here:
How to disable/enable the return key in a UITextField?
You can do it by accessing UISearchBar property.
let textFieldInsideSearchBar = searchBar.value(forKey: "searchField") as? UITextField
textFieldInsideSearchBar?.enablesReturnKeyAutomatically = false
By playing with enablesReturnKeyAutomatically property you can achieve your requirements.
Thanks.
This is how i do it:
if([searchbar.text length] == 0) {
[searchBar performSelector: #selector(resignFirstResponder)
withObject: nil
afterDelay: 0.1];
}
You can try this,
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{
if (searchText.length>=2) {
[Main_SearchBar resignFirstResponder];
// Do your code here
}
}
Here is the setting you're looking for:
searchBar.enablesReturnKeyAutomatically = true
You can try this
if([self.searchBar.text length] > 2)
{
[self.searchBar resignFirstResponder];
}

Interface Builder Tab Order Typing

I have noticed, in one of my views in an iPad app I am building the next button on the keyboard goes through all the UITextFields from left to right down the screen.
Is it possible somehow to make it go top to bottom then right, top to bottom?
So say I have to two long columns of text fields, I wan to go top to bottom not left to right, make sense?
Any help appreciated, thanks.
I don't think there is a way through IB, but you can do this way in code. You're not actually tabbing, you'd be using the return key.
Put this in your UITextField's delegate:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
BOOL shouldChangeText = YES;
if ([text isEqualToString:#"\n"]) {
// Find the next entry field
BOOL isLastField = YES;
for (UIView *view in [self entryFields]) {
if (view.tag == (textView.tag + 1)) {
[view becomeFirstResponder];
isLastField = NO;
break;
}
}
if (isLastField) {
[textView resignFirstResponder];
}
shouldChangeText = NO;
}
return shouldChangeText;
}
Found here: http://iphoneincubator.com/blog/tag/uitextfield
You'll want to implement UITextFieldDelegate's - (BOOL)textFieldShouldReturn:(UITextField *)textField method. An example of how to use this method to control the order is in this question.
I would like to elaborate on #sprocket's answer addressing the same issue. Just because something works out of the box doesn't mean you should stop thinking about a better way -- or even the right way -- of doing something. As he noticed the behavior is undocumented but fits our needs most of the time.
This wasn't enough for me though. Think of a RTL language and tabs would still tab left-to-right, not to mention the behavior is entirely different from simulator to device (device doesn't focus the first input upon tab). Most importantly though, Apple's undocumented implementation seems to only consider views currently installed in the view hierarchy.
Think of a form in form of (no pun intended) a table view. Each cell holds a single control, hence not all form elements may be visible at the same time. Apple would just cycle back up once you reached the bottommost (on screen!) control, instead of scrolling further down. This behavior is most definitely not what we desire.
So here's what I've come up with. Your form should be managed by a view controller, and view controllers are part of the responder chain. So you're perfectly free to implement the following methods:
#pragma mark - Key Commands
- (NSArray *)keyCommands
{
static NSArray *commands;
static dispatch_once_t once;
dispatch_once(&once, ^{
UIKeyCommand *const forward = [UIKeyCommand keyCommandWithInput:#"\t" modifierFlags:0 action:#selector(tabForward:)];
UIKeyCommand *const backward = [UIKeyCommand keyCommandWithInput:#"\t" modifierFlags:UIKeyModifierShift action:#selector(tabBackward:)];
commands = #[forward, backward];
});
return commands;
}
- (void)tabForward:(UIKeyCommand *)command
{
NSArray *const controls = self.controls;
UIResponder *firstResponder = nil;
for (UIResponder *const responder in controls) {
if (firstResponder != nil && responder.canBecomeFirstResponder) {
[responder becomeFirstResponder]; return;
}
else if (responder.isFirstResponder) {
firstResponder = responder;
}
}
[controls.firstObject becomeFirstResponder];
}
- (void)tabBackward:(UIKeyCommand *)command
{
NSArray *const controls = self.controls;
UIResponder *firstResponder = nil;
for (UIResponder *const responder in controls.reverseObjectEnumerator) {
if (firstResponder != nil && responder.canBecomeFirstResponder) {
[responder becomeFirstResponder]; return;
}
else if (responder.isFirstResponder) {
firstResponder = responder;
}
}
[controls.lastObject becomeFirstResponder];
}
Additional logic for scrolling offscreen responders visible beforehand may apply.
Another advantage of this approach is that you don't need to subclass all kinds of controls you may want to display (like UITextFields) but can instead manage the logic at controller level, where, let's be honest, is the right place to do so.

Simulate Tab Key Press in iOS SDK

When a hardware keyboard is used with iOS, pressing tab or shift-tab automatically navigates to the next or previous logical responder, respectively. Is there a way to do the same programmatically (i.e. simulating the tab key rather than keeping track of the logical order manually)?
As William Niu is right but you can also use this code explained below.
I have used this and got success.Now consider the example of UITextField...
You can use UITextView's delegate method -(BOOL)textFieldShouldReturn:(UITextField*)textField as explained below.
But before doing this you should have to give tag to each UITextField in an Increment order...(Increment order is not required necessary ,but as for my code it is required, you can also use decrement order but some code changes for doing this)
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [self.view viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
} else {
[textField resignFirstResponder];
}
return YES;
}
Hope this will work for you...
Happy coding....
You may define the "tab-order" using the tag property. The following post describes how to find the next tag index to go to for UITextFields,
How to navigate through textfields (Next / Done Buttons).
Here is a modified version of the code from that post. Instead of removing keyboard at the last tag index, this following code would try to loop back to the first tag index.
-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
NSInteger nextTag = textField.tag + 1;
// Try to find next responder
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
// Found next responder, so set it.
[nextResponder becomeFirstResponder];
return NO;
}
// Try to find the first responder instead...
// Assuming the first tag index is 1
UIResponder* firstResponder = [textField.superview viewWithTag:1];
if (firstResponder) {
// loop back to the first responder
[firstResponder becomeFirstResponder];
} else {
// Not found, so remove keyboard.
[textField resignFirstResponder];
}
return NO; // We do not want UITextField to insert line-breaks.
}
If you want an UI element other than UITextField, you should still be able to use the same logic, with a few more checks.
Not sure if this helps, but in the context of a UITextFields, if you implement UITextFieldDelegate, - (BOOL)textFieldShouldReturn:(UITextField *)textField will get called when the return key of the soft keyboard is pressed.
I've tried to hit directly on my laptop keyboard and it seemed to jump between all the textfields in the order in which you've added them to the view, but didn't go to any other types of fields (Buttons etc.).
key on the keyboard is simulating the key on the soft keyboard of the simulator, which works as expected.

Cursor location in a uitextfield

im using customkeyboard in my controller. but how to get the cursor location in the uitextfiled. my requirement is to enter a charecter in a desired location of textfield. is it possible?
Let's assume that you code is in a method on an object where self.textField is the UITextField in question.
You can find the current position of the cursor/selection with:
NSRange range = self.textField.selectedTextRange;
If the user has not selected text the range.length will be 0, indicating that it is just a cursor. Unfortunately this property does not appear to be KVO compliant, so there is no efficient way to be informed when it changes, however this should not be a problem in your case because you probably really only care about it when you are responding to user interaction with your custom keyboard.
You can then use (assuming newText holds the input from your custom keyboard).
[self.textField replaceRange:range withText:newText];
If you need to subsequently adjust the cursor/selection you can use:
self.textField.selectedTextRange = newRange;
For example, you may want to position the cursor after the text you inserted.
UPDATE:
In my original answer I failed to notice that I was leveraging a category I had added to UITextView:
- (void)setSelectedRange:(NSRange)selectedRange
{
UITextPosition* from = [self positionFromPosition:self.beginningOfDocument offset:selectedRange.location];
UITextPosition* to = [self positionFromPosition:from offset:selectedRange.length];
self.selectedTextRange = [self textRangeFromPosition:from toPosition:to];
}
- (NSRange)selectedRange
{
UITextRange* range = self.selectedTextRange;
NSInteger location = [self offsetFromPosition:self.beginningOfDocument toPosition:range.start];
NSInteger length = [self offsetFromPosition:range.start toPosition:range.end];
NSAssert(location >= 0, #"Location is valid.");
NSAssert(length >= 0, #"Length is valid.");
return NSMakeRange(location, length);
}
Then replace use self.textField.selectedRange instead of self.textField.selectedTextRange and proceed as I described.
Thanks to omz for pointing out my error.
Of course, you can work directly with UITextRange but, at least in my case, this proved to be rather ungainly.
The answer is that you can't get the current cursor location for all types of editing that can be done with the textfield. You can insert characters at the cursor with [textField paste], but the user can move the cursor, select and modify text, without a way to get notified where the cursor ended up.
You can temporarily paste a special character and search its position in the string, remove it, and then add the character you want to have there.
Swift
Get the cursor location:
if let selectedRange = textField.selectedTextRange {
let cursorPosition = textField.offsetFromPosition(textField.beginningOfDocument, toPosition: selectedRange.start)
}
Enter text at some arbitrary location:
let arbitraryValue: Int = 5
if let newPosition = textField.positionFromPosition(textField.beginningOfDocument, inDirection: UITextLayoutDirection.Right, offset: arbitraryValue) {
textField.selectedTextRange = textField.textRangeFromPosition(newPosition, toPosition: newPosition)
textField.insertText("Hello")
}
My full answer is here.

Can I hook into UISearchBar's Clear Button?

I've got a UISearchBar in my interface and I want to customise the behaviour of the the small clear button that appears in the search bar after some text has been entered (it's a small grey circle with a cross in it, appears on the right side of the search field).
Basically, I want it to not only clear the text of the search bar (which is the default implementation) but to also clear some other stuff from my interface, but calling one of my own methods.
I can't find anything in the docs for the UISearchBar class or the UISearchBarDelegate protocol - it doesn't look like you can directly get access to this behaviour.
The one thing I did note was that the docs explained that the delegate method:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;
is called after the clear button is tapped.
I initially wrote some code in that method that checked the search bar's text property, and if it was empty, then it had been cleared and to do all my other stuff.
Two problems which this though:
Firstly, for some reason I cannot fathom, even though I tell the search bar to resignFirstResponder at the end of my method, something, somewhere is setting it back to becomeFirstResponder. Really annoying...
Secondly, if the user doesn't use the clear button, and simply deletes the text in the bar using the delete button on the keyboard, this method is fired off and their search results go away. Not good.
Any advice or pointers in the right direction would be great!
Thanks!
Found the better solution for this problem :)
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if ([searchText length] == 0) {
[self performSelector:#selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
}
}
- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar{
[searchBar resignFirstResponder];
}
The answer which was accepted is incorrect. This can be done, I just figured it out and posted it in another question:
UISearchbar clearButton forces the keyboard to appear
Best
I've got this code in my app. Difference is that I don't support 'live search', but instead start searching when the user touches the search button on the keyboard:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
if ([searchBar.text isEqualToString:#""]) {
//Clear stuff here
}
}
Swift version handling close keyboard on clear button click :
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 {
performSelector("hideKeyboardWithSearchBar:", withObject:searchBar, afterDelay:0)
}
}
func hideKeyboardWithSearchBar(bar:UISearchBar) {
bar.resignFirstResponder()
}
You could try this:
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIView *view in searchBar.subviews){
for (UITextField *tf in view.subviews) {
if ([tf isKindOfClass: [UITextField class]]) {
tf.delegate = self;
break;
}
}
}
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
// your code
return YES;
}
I would suggest using the rightView and rightViewMode methods of UITextField to create your own clear button that uses the same image. I'm assuming of course that UISearchBar will let you access the UITextField within it. I think it will.
Be aware of this from the iPhone OS Reference Library:
If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.
So you'll probably also need to disable the original clear button.
Since this comes up first, and far as I can see the question wasn't really adequately addressed, I thought I'd post my solution.
1) You need to get a reference to the textField inside the searchBar
2) You need to catch that textField's clear when it fires.
This is pretty simple. Here's one way.
a) Make sure you make your class a , since you will be using the delegate method of the textField inside the searchBar.
b) Also, connect your searchBar to an Outlet in your class. I just called mine searchBar.
c) from viewDidLoad you want to get ahold of the textField inside the searchBar. I did it like this.
UITextField *textField = [self.searchBar valueForKey:#"_searchField"];
if (textField) {
textField.delegate = self;
textField.tag = 1000;
}
Notice, I assigned a tag to that textField so that I can grab it again, and I made it a textField delegate. You could have created a property and assigned this textField to that property to grab it later, but I used a tag.
From here you just need to call the delegate method:
-(BOOL)textFieldShouldClear:(UITextField *)textField {
if (textField.tag == 1000) {
// do something
return YES;
}
return NO;
}
That's it. Since you are referring to a private valueForKey I can't guarantee that it will not get you into trouble.
Best solution from my experience is just to put a UIButton (with clear background and no text) above the system clear button and than connect an IBAction
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = #"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
Wasn't able to find a solution here that didn't use a private API or wasn't upgrade proof incase Apple changes the view structure of the UISearchBar. Here is what I wrote that works:
- (void)viewDidLoad {
[super viewDidLoad];
UITextField* textfield = [self findTextFieldInside:self.searchBar];
[textfield setDelegate:self];
}
- (UITextField*)findTextFieldInside:(id)mainView {
for (id view in [mainView subviews]) {
if ([view isKindOfClass:[UITextField class]]) {
return view;
}
id subview = [self findTextFieldInside:view];
if (subview != nil) {
return subview;
}
}
return nil;
}
Then implement the UITextFieldDelegate protocol into your class and overwrite the textFieldShouldClear: method.
- (BOOL)textFieldShouldClear:(UITextField*)textField {
// Put your code in here.
return YES;
}
Edit: Setting the delegate on the textfield of a search bar in iOS8 will produce a crash. However it looks like the searchBar:textDidChange: method will get called on iOS8 on clear.