Extra call to setMarkedText:selectedRange in iOS7? - ios

In my app I have UITextInput implemented so I can handle multi stage key input (Japanese, Chinese keyboards) for my custom text view. I'm noticing in iOS7, when you have some text that is marked, and you tap one of the suggestions above the keyboard to replace it, that setMarkedText:selectedRange is called twice: once where it replaces the marked text with the string selected from the panel above the keyboard (as you'd expect), and once where an empty string is sent as the parameter. In iOS6, it's only called once.
My questions are, is there a reason it's doing this? And how should I adjust my setMarkedText:selectedRange to account for this (listed below):
- (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange
{
NSRange selectedNSRange = self.textView.selectedTextRange;
NSRange markedTextRange = self.textView.markedTextRange;
if (markedTextRange.location != NSNotFound)
{
if (!markedText)
markedText = #"";
[self.text replaceCharactersInRange:markedTextRange withString:markedText];
markedTextRange.length = markedText.length;
}
else if (selectedNSRange.length > 0)
{
[self.text replaceCharactersInRange:selectedNSRange withString:markedText];
markedTextRange.location = selectedNSRange.location;
markedTextRange.length = markedText.length;
}
else
{
[self.text insertString:markedText atIndex:selectedNSRange.location];
markedTextRange.location = selectedNSRange.location;
markedTextRange.length = markedText.length;
}
selectedNSRange = NSMakeRange(selectedRange.location + markedTextRange.location, selectedRange.length);
self.textView.contentText = self.text;
self.textView.markedTextRange = markedTextRange;
self.textView.selectedTextRange = selectedNSRange;
}
My first instinct is to put an if statement around the contents saying
if markedText != #""
but I'm not sure if I'd be messing up some other cases. Does anyone have any suggestions on how to account for this change??

The guy from DTS recommended this solution:
- (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange
{
...
if (markedText == nil || markedText.length == 0 )
{
[self unmarkText];
}
}
And it seems to work fine.

Related

Xcode label text from date picked

I have a calendar view that displays the days of the month, and I would like to put the output into my label but am unable to. I can NSLog it but not transfer it into a label. Here is my code:
- (void)calendarView:(DSLCalendarView *)calendarView didSelectRange:(DSLCalendarRange *)range {
if (range != nil) {
NSLog( #"%ld/%ld", (long)range.startDay.month, (long)range.endDay.day);
NSString *datechosen = ( #"%ld/%ld", range.startDay.month,range.endDay.day);
DatePicked.text = datechosen;
}
Any help would be appreciated.
try something like this
if(DatePicked)
{
DatePicked.text = datechosen;
}
else
{
NSLog(#"hey look, DatePicked is not connected to an IBOutlet");
}
DatePicked.text = [NSString stringWithFormat:"%ld/%ld",(long)range.startDay.mont, (long)range.endDay.day];
Pleaes read Formatting String Objects.

IOS String length comparison issue

I'm struggling with an if Comparison - I basically want to make two comparisons - both of which need to pass - Firstly a basic if a string variable is equal to 'rec' and secondly if a strings character limit is not equal to zero.
I've tried various combinations - but this is where i'm at at the mo..
ArticleObject *A = [self.articleArray objectAtIndex:indexPath.section];
NSInteger imglength = [A.arImage length];
if([A.arRec isEqual: #"rec"] ) && (imglength !=Nil){
return 195;
}
else return 50;
I get an expected identifier error on the (imglength comparison - as in this screen shot
Can anyone shed any light for me please?
There are several things you should change:
ArticleObject *A = self.articleArray[indexPath.section];
NSInteger imglength = [A.arImage length];
if (imglength && [A.arRec isEqualToString:#"rec"]) {
return 195;
} else {
return 50;
}
Don't use Nil (or nil) with primitive types.
Your parentheses are messed up:
if([A.arec isEqualToString:#"rec"] && (imglengyb !=Nil))
^--------------//here
Maybe a better way would be:
if([A.arec isEqualToString:#"rec"] && [[A.arImage length] != 0])

Phone number format ios

In my app I am taking phone number as an input from user. Number should be in US format. I want to display it like (555)-888-888 dynamically. For example when user starts to input number when he reaches to 4 digit it shows number like this (555)-4 and so on. I tried to replaceString method but i found that it will not work.
Look at NBAsYouTypeFormatter class of libPhoneNumber-iOS library.
You create new instance of NSAsYouTypeFormatter with your US region code given:
NBAsYouTypeFormatter *asYouTypeFormatter = [[NBAsYouTypeFormatter alloc] initWithRegionCode:REGION_CODE_STRING];
Then every time user changes the phone number you call:
- (NSString*)inputDigit:(NSString*)nextChar;
or
- (NSString*)removeLastDigit;
Returned NSString from this two methods is your dynamically formatted phone number.
I am going to explain from scratch. So, new users can get the way from start.
Download libPhoneNumber-iOS library from here. At the bottom side of the page of that link, you will find what files you need to add to your project.
Now, follow below steps to implement.
(1) Import files in the view controller where you need your textfield to be formatted.
#import "NBPhoneMetaDataGenerator.h"
#import "NBPhoneNumberUtil.h"
#import "NBAsYouTypeFormatter.h"
and make instance of type NBAsYouTypeFormatter in header file:
NBAsYouTypeFormatter *asYouTypeFormatter;
(2) In the viewDidLoad method of that view controller, initialize that object taken earlier:
asYouTypeFormatter = [[NBAsYouTypeFormatter alloc] initWithRegionCode:#"IN"];
Note: #"IN" is for India. You can set it to anything you want. Refer to plist file that will be included in libPhoneNumber-iOS library to view full list of region codes.
(3) In delegate method of UITextField, dynamically manage text of yout textfield.
#pragma mark
#pragma mark - Phone Number textfield formatting
# define LIMIT 18 // Or whatever you want
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Just allow 18 digits
if(!(([string length] + range.location) > LIMIT))
{
// Something entered by user
if(range.length == 0)
{
[txtNumber setText:[asYouTypeFormatter inputDigit:string]];
}
// Backspace
else if(range.length == 1)
{
[txtNumber setText:[asYouTypeFormatter removeLastDigit]];
}
}
return NO;
}
Hope it helps !!!
I found a solution that I wanted to share because, even with the solutions previously presented here, I had a hard time finding how to make it work.
I have a tableView whose cells include a textField. One of this cells bear the phone number. It can be already filled-in in some cases, or not.
This is in Swift by the way.
Make sure your bridging header file nameOfYourProject-Bridging-Header includes the following line:
#import "NBAsYouTypeFormatter.h"
Declare a property for the NBAsYouTypeFormatter:
private var phoneFormatter: NBAsYouTypeFormatter!
in viewDidLoad, or didSet of a property, initialize the NBAsYouTypeFormatter with the country code:
// yourRegionCode is a 2-digit country code (ISO 3166)
phoneFormatter = NBAsYouTypeFormatter(regionCode: yourRegionCode)
Declare your viewController as a TextFieldDelegate and implement function shouldChangeCharactersInRange:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Phone number cell
if cellContainsPhoneNumber { // This is specific to your own tableView
// Formatting phone number as you type
let textWithoutSpaces = textField.text.stringByReplacingOccurrencesOfString(" ", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
phoneFormatter.inputString(textWithoutSpaces) // This is the initial value of the phoneFormatter each time the delegate method is called
let formattedNumber: String!
if string == "" {
formattedNumber = phoneFormatter.removeLastDigit()
} else {
formattedNumber = phoneFormatter.inputDigit(string)
}
// set the textField text with the new formattedNumber
textField.text = formattedNumber
return false
}
return true
}
This way, it works exactly as Apple's contact edition mechanism.
Let me know if this helped you.
Here's an updated snippet that generally works for me (Swift 2.0):
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Allow up to 18 chars
if !(string.characters.count + range.location > 18) {
if range.length == 0 {
// original text didn't change
textField.text = phoneFormatter?.inputDigit(string)
} else if range.length == 1 {
// user pressed backspace
textField.text = phoneFormatter?.removeLastDigit()
} else if range.length == textField.text?.characters.count {
// text was cleared
phoneFormatter?.clear()
textField.text = ""
}
}
return false
}
The main thing that changed was it allows for the user to press the "Clear" button or Select All -> Clear.
There are some edge cases such as the user editing specific digits in the phone number which this doesn't handle but could be easily added.
Here is a solution using libPhoneNumber that also handles the non trivial cases of editing in the middle of the number, cutting and pasting, selection and typing. It keeps the cursor stable and does not behave unexpectedly.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(range.location == textField.text.length && range.length == 0)
{
// Something added at end
textField.text = [numberFormatter inputDigit:string];
}
else if(range.location == textField.text.length-1 && range.length == 1)
{
// Backspace at end
textField.text = [numberFormatter removeLastDigit];
} else {
// Other modification in middle
NSString* input = textField.text;
// New cursor position after modification
NSUInteger cursorIdx = range.location + string.length;
// If backspacing to delete a format character - just reposition the cursor.
BOOL backspaceOnly = range.length == 1 && string.length == 0 && !isdigit([input characterAtIndex:range.location]);
if(!backspaceOnly) {
// make the modification, reformat the number
input = [input stringByReplacingCharactersInRange:range withString:string];
[numberFormatter clear];
BOOL rememberCursorPos = NO;
NSString* text;
// reinput the number to the formatter
// remembering the first digit position at or after the cursor
for (int i = 0; i < input.length; ++i)
{
if(i == cursorIdx) {
rememberCursorPos = YES;
}
char digit = [input characterAtIndex:i];
switch(digit) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if(!rememberCursorPos) {
text = [numberFormatter inputDigit:[NSString stringWithFormat:#"%c", digit]];
} else {
text = [numberFormatter inputDigitAndRememberPosition:[NSString stringWithFormat:#"%c", digit]];
rememberCursorPos = NO;
}
break;
}
}
// reformat the number
textField.text = text;
// get updated cursor position (formatter position starts at 1)
cursorIdx = numberFormatter.getRememberedPosition - 1;
}
// reposition the cursor
UITextPosition* position = [textField positionFromPosition:textField.beginningOfDocument offset:cursorIdx];
textField.selectedTextRange = [textField textRangeFromPosition:position toPosition:position];
}
return NO;
}
You can use this library for formatting input during typing https://github.com/luximetr/AnyFormatKit
Example
let textInputController = TextInputController()
let textInput = TextInputField() // or TextInputView or any TextInput
textInputController.textInput = textInput // setting textInput
let formatter = TextInputFormatter(textPattern: "### (###) ###-##-##", prefix: "+12")
textInputController.formatter = formatter // setting formatter
In this case TextInputController will format text in your textField or textView.

format uiTextField for phoneNumber and date

I'm using parse to store my data. I have a bunch of UITextField for user registration inside a view controller.
Now, in my phone text field, how do I format the text field to show the following depending on the total length.
+55 (21) 99999-9999 = 13 numbers
+55 (21) 9999-9999 = 12 numbers
I want to accept both 12 and 13 numbers and show the formatted phone in the textfield.
Now, for saving it to parse, I would like to save the formatted number with characters +, (, ), -.
I would also like to format my date text field to dd/mm/yyyy. Can anyone help me?
Thanks.
UPDATE
Ok, so I did the following:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (_telefoneTextField.text.length == 0)
_telefoneTextField.text = [NSString stringWithFormat:#"(%#",_telefoneTextField.text];
if (_telefoneTextField.text.length == 3)
_telefoneTextField.text = [NSString stringWithFormat:#"%#) ",_telefoneTextField.text];
if (_telefoneTextField.text.length == 9)
_telefoneTextField.text = [NSString stringWithFormat:#"%#-",_telefoneTextField.text];
return YES;
}
and
else if (textField == self.telefoneTextField)
{
if (_telefoneTextField.text.length == 14)
{
NSLog(#"Telefone sem o 9");
[self.nascimentoTextField becomeFirstResponder];
}
if (_telefoneTextField.text.length == 15)
{
NSLog(#"Telefone COM o 9");
NSMutableString *telefone = [[NSMutableString alloc] initWithString:_telefoneTextField.text];
[telefone deleteCharactersInRange:NSMakeRange(9,1)];
[telefone insertString:#"-" atIndex:10];
NSLog(#"%#", telefone);
_telefoneTextField.text = telefone;
[self.nascimentoTextField becomeFirstResponder];
}
else
{
NSLog(#"Telefone NAO esta no formato");
}
}
now, it works like I wanted. It changes the format in real time when the user is typing and when finish editing it checks to see how many chars, in case of 15, it changes the format again.
Now, one thing I couldn't do: How can I delete the phone number using the keyboard, I mean, it does't delete the numbers before the "-"character.
Thanks.
The best way to implement this by using regular expressions. See the following discussion
Regex not working correctly on iOS

creating a prefix NSString using two NSStrings

I have instructions to make a prefix method that takes two strings for each position where mask = 0 and the first string = second string up until these conditions are not meet that is your prefix NSString.
I made my attempt but for some reason my prefix string is returning as null and I was hoping i could get some help.
here is my method
- (void)prefixCalculation:(NSString *)seriesStart SeriesEnd:(NSString *)seriesEnd {
// call this method when loading the view to get everything set up
NSLog(#"start %#", seriesStart);
NSLog(#"end %#", seriesEnd);
// allocate values so you can use this to create the UITextField
seriesStartString = seriesStart;
seriesEndString = seriesEnd;
// set prefix string
for (int i = 0; i <= seriesStartString.length ; i++) {
unichar c1 = [seriesStartString characterAtIndex:i];
unichar c2 = [seriesEndString characterAtIndex:i];
if (c1 != c2) {
break;
}
else if (c1 == c2) {
NSString *str = [NSString stringWithFormat: #"%C", c1];
[prefixString appendFormat:#"%#",str];
}
}
NSLog(#"prefix %#", prefixString);
}
I am not sure what I am doing wrong but prefixString which is a NSMutableStrong comes back as null, any help would be greatly appreciated.
Since in your code you don't show the initialization of prefixString, I take a guess and suggest you to check whether you initialized it or not.
If that's not the case, prefixString is nil and sending messages to it will fail silently.

Resources