CustomTextField - AutoComplete/AutoCorrect won't dismiss on tap - ios

So I created a custom text view using core graphics and have it conformed to the UITextInput and UITextInputTraits protocols. Everything works fine except for one weird/annoying behavior. The keyboard correctly displays auto correct suggestions, but when the use taps on a suggestion labelled with an 'X', it doesn't dismiss the suggestion but instead inserts the suggestion. I've checked, and in all other programs tapping on the a suggestion with an 'X' dismisses the suggestion. How do I fix this?
In my custom text view I have the following iVars:
//UITextInputTraits
UITextAutocapitalizationType _uiAutoCap;
UITextAutocorrectionType _uiAutoCorrect;
UITextSpellCheckingType _uiSpellCheck;
UIKeyboardType _uiKeyboard;
UIKeyboardAppearance _uiKeyboardAppearance;
UIReturnKeyType _uiReturnType;
BOOL _uiEnableAutoReturn;
BOOL _uiSecureText;
Which are synthesized to the appropriate TextInputTraits properties:
#synthesize autocapitalizationType=_uiAutoCap, autocorrectionType=_uiAutoCorrect, spellCheckingType=_uiSpellCheck, keyboardType=_uiKeyboard, keyboardAppearance=_uiKeyboardAppearance, returnKeyType=_uiReturnType, inputDelegate=_uiTextDelegate, enablesReturnKeyAutomatically=_uiEnableAutoReturn, secureTextEntry=_uiSecureText;
And they're initialized with the following default values:
_uiAutoCorrect = UITextAutocorrectionTypeDefault;
_uiSpellCheck = UITextSpellCheckingTypeDefault;
_uiKeyboardAppearance = UIKeyboardAppearanceDefault;
_uiAutoCap = UITextAutocapitalizationTypeNone;
_uiReturnType = UIReturnKeyDefault;
_uiEnableAutoReturn = NO;
_uiSecureText = NO;
_uiKeyboard = UIKeyboardTypeDefault;
Any ideas?

Edit: Possible answer
When you tap to close the suggestion, your tap is likely intercepted by your view first which presumably changes the selected range of the text (which causes UITextInput to accept the suggestion). Not the best solution, but UITextInput calls
- (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction;
when it wants to make a suggestion, so, you could have an ivar (BOOL) that stores whether or not there is a suggestion (make its value NO whenever a UIKeyInput method is called and YES when the textStyling method is called). Then, modify your gesture recognizer so that it will not change the selection if the aforementioned ivar is YES and the tap is in the rect of the suggestion box (You could get this rect by doubling the height of the rect returned from - (CGRect)firstRectForRange:(UITextRange *)range;). Hope that works.
Edit: you should just be able to implement the UIGestureRecognizerDelegate method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
So that it only receives a touch if touch.view == (yourTextView)
I am having the same issue, and don't yet have a solution; however, I do believe that you should conform to UITextInputTraits by creating functions that return the value you would like for the property. Example: for the trait UITextAutoCorrectionType to have a value of UITextAutocorrectionTypeDefault, you should provide an accessor method:
- (UITextAutocorrectionType)autocorrectionType {
return UITextAutocorrectionTypeDefault;
}

Related

Pass tap event to the subview iOS

I have a TTTAttributed Label which detect link on it by self and I have a tap gesture on it which perform some action.
The problem is when I have a tap gesture on it then it cant be able to open the link It only can perform the action by tap gesture But I want that if the text is link type then my tap gesture do forward the tap to the link detection of TTTAttributed label.
Your effort is truly appreciate. Thanks in advance.
label.enabledTextCheckingTypes = NSTextCheckingTypeLink; // Automatically detect links when the label text is subsequently changed
label.delegate = self; // Delegate methods are called when the user taps on a link (see `TTTAttributedLabelDelegate` protocol)
label.text = #"Google it! (https://www.google.co.in/)"; // Repository URL will be automatically detected and linked
NSRange range = [label.text rangeOfString:#"Google"];
[label addLinkToURL:[NSURL URLWithString:#"http://google.com/"] withRange:range]; // Embedding a custom link in a substring
Set the delegate of the TTTAttributed label and when the user will tap on the label its delegate method will get invoked.
(void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url
Handle your event in this delegate method.
Check for the [UITapGestureRecognizer state] property in your tap gesture recogniser delegate method. Based on the state value, pass on the event to following touch delegate methods of your TTTAttributedLabel object, after you are done with your custom tap gesture handling:
- touchesBegan:withEvent:
- touchesMoved:withEvent:
- touchesEnded:withEvent:
- touchesCancelled:withEvent:
This should take care of executing the default behaviour of the TTTAttributedLabel class.

if UITextField clicked

I am doing an application and I need to detect when a UITextField is clicked. I tried using touchesBegan but it doesn't react to textfield when clicked, only outside of it. I am only starting with Objective-C so if you give me advice, please let it be detailed. Thank you.
You can try the delegate method:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// Do something
}
As long as the text field has interaction enabled and is editable. However, a second tap will not be detected if the text field is already the first responder.
Set the textview's delegate property to an object that overloads the textViewShouldBeginEditing: function. You could also use the textFieldDidBeginEditing: function of the same delegate.

Check If a method Has Been Called and Do Not Call It Again

In touchesBegan I call a method [self animateWindow]; when I touch a UILabel. It animates and brings the window from top. When I touch that UILabel again, I don't want that method [self animateWindow]; to be called again because it animates and brings the window again which is already being displayed. How do I do this? Any help? I tried searching all over, couldn't find the exact answer.
Just create an instance variable which is a BOOL as use it as a flag:
#property (assign, monatomic) BOOL hasAnimated;
- (void) touchesBegan...
{
if (!self.hasAnimated) {
[self animateWindow];
self.hasAnimated = YES;
}
}
Based on your expanded description in the comments, you're going to need to add some more logic which checks which label is actually being touch. A better solution may be to use gesture recognisers on each of the labels. Then you have direct access to the view of the gesture recognizer to check which label it was (perhaps using the tag) and you can add and remove gestures depending on what state you're in (only some labels should respond to touches and they should show or hide).

How To Stop keyBoard Appearence at click event on UITextview?

In my IPad Application i am using TextView only for Text Displaying.As i need to display a Larger Text Thats Why i am using UITextview due to its Scrolling Property instead of using UILabel.
In my application i do not need to edit Text in UITextview ,but problem for me is that when i click on Textview for scrolling the keyboard appear its hide my textview so i want that my keyboard is never appear on click event.i make a search but not find any Suitable solution.Any help will be appriated.Thanx
NEW ANSWER (previous one was not working properly)
OK so since that is not working because it disables scrolling also, you should try to:
Implement UITextFieldDelegate protocol
In your view controller add the text
#interface YourViewController () <UITextViewDelegate>
In viewDidLoad set yourself as a delegate:
yourUITextView.delegate = self;
Implement the delegate method below:
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
return NO;
}
When the textview is about to edit the text, this method will be called automatically. It returns no, so the editing won't start.
It is very important that you undo the changes from the previous answers: Do not set the editable field to NO
I tried it and it's working. Hope it helps!
OLD ANSWER
when you declare the variable, or in your viewdidload method, set the editable property to NO:
yourUITextView.editable = NO;
or
[yourUITextView setEditable:NO]
That should prevent the keyboard from appearing.
Go to .XIB file and you can uncheck behavior editable or programmatically
textView.editable = NO;

How do you set the tab order in iOS?

Is there a way (either in IB or code) to set the tab order between text fields in a view?
Note that I'm not talking about the next form field after the return (or "Next") button is pressed -- many bluetooth keyboards have a tab key, which seems to cycle through the fields in completely different order. In my particular case, this order doesn't correspond to the fields' position in the view or even the order in which the fields were added. Modifying the xib file by hand to change the NSNextKeyView doesn't seem to make a difference either.
Does anyone know how to change this order?
#sprocket's answer was only somewhat helpful. 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.
I'm interested in solving the same problem, although so far the default order, which appears to be left to right, then top to bottom, is the one I want.
I tested the hypothesis that the cursor moves in depth-first order through the tree of subviews and superview, but that is not true. Changing the order of subviews without changing their location didn't change the order of fields traversed by tab presses.
One possibly useful feature is that the text field delegate's textFieldShouldBeginEditing method appears to be called for every text field in the application's window. If that returns NO, then the text field won't be chosen, so if you can define your desired order and make only the right one return YES, that might solve your problem.
This is how you set the tab order on iOS:
http://weaklyreferenced.wordpress.com/2012/11/13/responding-to-the-tab-and-shift-tab-keys-on-ios-5-ios-6-with-an-external-keyboard/
The Tab key behaviour in ios will be as follows:-
when u press tab on external keyboard- the control traverses across all the textfields in that screen by calling only shouldBeginEditing method where its return value is also determined by Apple which cant be override.
After scanning all the fields it calculates nearest x positioned Textfield relative to view offset from the current Textfield and then nearest Y Positioned Field.
Also can't be done anything until control comes to textFieldDidBeginEditing method.
Reason for apple's restriction might be to let devs to follow the guidelines of UI where next responder of field should be it's closest positioned Field rather than any other field .
Register a UIKeyCommand to detect the tab key pressed. I did this in my current view controller.
self.addKeyCommand(UIKeyCommand(input: "\t", modifierFlags: [], action: #selector(tabKeyPressed)))
Inside the key tabKeyPressed handler find your current active field then set your next responder. orderedTextFields is an array of UITextField in the tab order I want.
func tabKeyPressed(){
let activeField = getActiveField()
if(activeField == nil){
return
}
let nextResponder = getNextTextField(activeField!)
nextResponder?.becomeFirstResponder()
}
func getActiveField() -> UITextField? {
for textField in orderedTextFields {
if(textField.isFirstResponder()){
return textField
}
}
return nil
}
func getNextTextField(current: UITextField) -> UITextField? {
let index = orderedTextField.indexOf(current)
if(orderedTextField.count-1 <= index!){
return nil
}
return orderedTextField[index! + 1]
}
You can do this by setting the tag for each textfield and handling this in the textfieldShouldReturn method.
See this blogpost about it:
http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen
The only way I've found to uniquely detect a Tab keystroke from a physical keyboard, is implementing the UIKeyInput protocol's insertText: method on a custom object that canBecomeFirstResponder.
- (void)insertText:(NSString *)text {
NSLog(#"text is equal to tab character: %i", [text isEqualToString:#"\t"]);
}
I didn't get this to work while subclassing UITextField, unfortunately, as UITextField won't allow the insertText: protocol method to get called.
Might help you on the way, though..
I solved this by subclassing UITextField as NextableTextField. That subclass has a property of class UITextField with IBOutlet a hookup.
Build the interface in IB. Set the class of your text field to NextableTextField. Use the connections Inspector to drag a connection to the 'next' field you want to tab to.
In your text field delegate class, add this delegate method...
- (BOOL)textFieldShouldReturn:(UITextField *) textField
{
BOOL didResign = [textField resignFirstResponder];
if (!didResign) return NO;
if ([textField isKindOfClass:[NextableTextField class]])
dispatch_async(dispatch_get_current_queue(), ^{ [[(NextableTextField *)textField nextField] becomeFirstResponder]; });
return YES;
}
BTW - I didn't come up with this; just remember seeing someone else's idea.

Resources