Convert UITextView text into [String], separating paragraphs into the Strings [duplicate] - ios

This question already has answers here:
Split a String into an array in Swift?
(40 answers)
Closed 6 years ago.
I have an iOS project using Xcode 7 and Swift3. I have a UITextView that enables the user to save that text via NSUserDefaults into a String variable. However, if the user has a lot of text that includes paragraphs, when they save the text, it compiles it together to be one big long string of text, not recognizing the original paragraphs.
How do I have the string of text in essence recognize there are paragraphs and then make them into separate strings that are part of a [String]? Is this possible?
Example of text:
This is a complex task for me.
I don't know how to do this.
After the user names this text, which is two separate small paragraphs, it would save to NSUserDefaults as:
This is a complex task for me. I don't know how to do this.
I know rather than save a String to NSUserDefaults I need to instead use a [String], but I still can't figure out how to take a UITextView and convert it's text into the string array, separating each paragraph into a separate String.

I looked at the reference in Apple's documents concerning paragraphs. I then took the advice and did the following:
// Assign the text to an NSString Variable
var newTextNS = instructions.text! as NSString
// Separate the text paragraphs into String Variables
var newArray = newTextNS.components(separatedBy: "\n")
I then had an array with all the different paragraphs as separate String variables.
Worked just as I needed it too.

Please check Apple's reference Words, Paragraphs, and Line Breaks
NSArray *arr = [myString componentsSeparatedByString:#"\n"];
or
NSString *string = /* assume this exists */;
NSRange range = NSMakeRange(0, string.length);
[string enumerateSubstringsInRange:range
options:NSStringEnumerationByParagraphs
usingBlock:^(NSString * _Nullable paragraph, NSRange paragraphRange, NSRange enclosingRange, BOOL * _Nonnull stop) { // ... }];
Hope it helps.

Related

How to change the color of a word that was replaced

Think of a grammar checker. If I type in "I should of known the show was today." it would make it into "I should have known it was today." There is a grammar checker app that will make the replaced part into the color red. I want to know how to do that.
BEFORE you link me to some other question, I've seen them, and they all talk about range. Range requires a set amount of characters that must be put, but you can see how this is a problem when you have over 100 phrases that would change if they were corrected. I keep seeing NSMutableString but that also has range.
For example, here is the code that turns the user input(whatever he types in) into a string.
NSString *input = _inputTextView.text;
And here is an example of a word that is being changed.
inpput = [input stringByReplacingOccurencesOfString:#"(?wi)\\bshould of\\b" withString:#"should have" options:NSRegularExpressionSearch range:NSMakeRange(0, [input length])];
And this is the code that makes the corrected string show in another UITextView:
_outputTextView.text = input;
For example:
User input: I should of gone to the store.
Output: I should have gone to the store.
Basically in the easiest way possible, is there a code I can put inside the [] on the above code to change the color? Like an options: color: or something?
This one interested me so I worked out a quick solution to it.
I tested this and it seems to work pretty well. You need to use attributed strings in order to store the color attributes.
// define what you want to replace and what you want to replace it with
NSString *stringToReplace = #"(?wi)\\bshould of\\b";
NSString *newString = #"should have";
// get a mutable attributed string so that the color can be used as an attribute
NSMutableAttributedString *attributedInput = _inputTextView.attributedText.mutableCopy;
NSRange rangeToReplace;
do
{
// check for occurrence
rangeToReplace = [[attributedInput string] rangeOfString:stringToReplace options:NSRegularExpressionSearch];
if(rangeToReplace.location != NSNotFound)
{
// create the range we're going to color
NSUInteger locationToBeReplaced = rangeToReplace.location;
NSUInteger replacedLength = newString.length;
NSRange rangeToAddAttribute = NSMakeRange(locationToBeReplaced, replacedLength);
// replace old string
[attributedInput replaceCharactersInRange:rangeToReplace withString:newString];
// color new string
[attributedInput addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:rangeToAddAttribute];
}
}
while(rangeToReplace.location != NSNotFound); // continue until all occurrences have been replaced

Displaying two different types of variables in one label string

I am trying to make a CCLabelTTF display a string and an integer together. Like this:
Your score is 0.
I've tried a few things but I usually get the warning Data argument not used by format string, and the label doesn't output the correct statements.
I am just trying to figure out the format in which to put these in and searching Google hasn't provided much, as I'm not really sure what exactly to search.
I've tried
label.string = (#"%#", #"hi", #"%d", investmentsPurchased);
but obviously that isn't correct. How would I do this?
Thanks.
(I assume this is ObjC and not Swift.) Try something like this:
label.string = [NSString stringWithFormat:#"hi %d", investmentsPurchased];
You use a single format string, which contains static text and replacement tokens (like %d) for any replacement variables. Then follows the list of values to substitute in. You can use multiple variables like:
label.string = [NSString stringWithFormat:#"number %d and a string %#", someInteger, someString];
use NSString newString = [NSString stringWithFormat:#"hello %#", investmentsPurchased];
in short: use stringWithFormat

How do I replace words in a text field to change into other words in another text field? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
This is for Xcode 5 and for iOS apps; I'm working with Objective-C code.
Okay so for example, if I put into the first text field "I was born in 1995." and when I click a convert button, I want it to say "I was conceived circa 1995." How can I go about doing that? The problem I'm having is being able to replace a word with another word.
Like, how do I return whats in the text field to be replace the possible words into whatever the person types in? Sort of like a translator app, the way it replaced words.
My question concerns that if the user were to type anything into the text field, then it would rephrase it for him with words that have synonyms to be other words.
Yes, it is about replacing substrings, but it will replace whatever the user types into it.
I understand that stringByReplacingOccurrencesOfString:withString: makes sense, but what would go after that to apply to whatever the user typed in?
Basically, let's say a translator app. If I type in: "I am very smart." it would rephrase to: "I am very intellectual." It has to deal with whatever the user types in.
If the string is in NSString, you can use the stringByReplacingOccurrencesOfString:withString: method.
NSString *str = #"I was born in 1995.";
str = [str stringByReplacingOccurrencesOfString:#"born in" withString:#"conceived circa"];
Not sure though if this is what you mean.
Given a word, you want to find words with similar meaning. You will need a lot of data - arrays of words grouped by meaning. A given word may appear in several of these arrays depending on context (one reason why machine interpretation of language is difficult...).
The easy way to separate the string into words is to use componentsSeparatedByString: with a string of #" ". After that you need to identify which strings you want to try and replace. Whether or not they return anything useful using the dictionary described below is one way to decide.
A specific word can be used as a key into a dictionary returning an array of arrays. Let's say the word is "iron". When you use that as a key in your master dictionary it returns an array containing three arrays. In one are all the words (as NSStrings) that mean "elemental iron", in another are all the words that mean "smooth clothes with a hot tool" such as "press" or "smooth", in another are tool-like uses such as "shooting iron", "branding iron" etc.
The hardest thing you have to do is identify the context and choose the right array, or else you end up generating nonsense.
Once you have identified context you can choose any other word from the selected array, substitute it in the sentence, and then process other words you want to substitute.
To separate a string such as
NSString *string = #"I am very smart."
you would use
NSArray *words = [string componentsSeparatedByString:#" "];
you can iterate over the words with
for(NSString *word in words) {
// do something with NSString *word here
}
Here's a quick look at building the master dictionary - it hasn't been run but I'm sure someone will spot if there's a mistake...
// NSArray *arrayOfArraysOfSimilarWords; <- populated elsewhere, as shown below
// array
// |
// - "cat", "feline", "pussycat", "gato"
// - "shoe", "pump", "clog", "trainer"
NSMutableDictionary *masterDictionary = [NSMutableDictionary dictionary];
for(NSArray *wordArray in arrayOfArraysOfSimilarWords) {
for(NSString *word in wordArray) {
NSArray *arrayOfWordArrays = masterDictionary[word];
if(arrayOfWordArrays == nil) {
masterDictionary[word] = #[wordArray];
} else {
// entry exists
NSSet *wordArraySet = [NSSet setWithArray:arrayOfWordArrays];
if([wordArraySet containsObject:wordArray]) {
// this word array is already returned as a result for this word
continue;
}
// there is no entry for this word array yet - add an array containing the current word array
NSMutableArray *newArrayOfWordArrays = [NSMutableArray arrayWithArray:arrayOfWordArrays];
[newArrayOfWordArrays addObject:wordArray];
masterDictionary[word] = newArrayOfWordArrays;
}
}
}
If you're trying to do in place replacement of strings while the user types you will need probably to use the UITextFieldDelegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
This is called on every character insertion (or text paste) in a UITextField.
Pseudo code (because I'm writing this off the top of my head):
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSMutableString *proposedText = [textField.text mutableCopy];
[proposedText replaceCharactersInRange:range withString:string];
if (proposedText contains word I want to replace)
{
replace word in proposedText
textField.text = proposedText;
textField.selectedTextRange = new selection
return NO;
}
return YES;
}
Hope this helps.

Voice over doesn't read phone number properly

I have phone number in below format
1-1xx-2xx-9565
Currently VO read it as "One (pause) One x x (pause) two x x (pause) minus nine thousand five hundred sixty five".
VO should read it as "One (pause) One x x (pause) two x x (pause) nine five six five".
What could be the problem? Is this wrong phone format?
Let's break down what is happening. VoiceOver doesn't know that the text you are presenting is a phone number and treats it like a sentence of text. In that text it tries to find distinct components and read them appropriately. For example, the text "buy 60 cantaloupes" has 3 components, "buy", "60", and "cantaloupes". The first is text and is read as text, the second is purely numerical and is best read out as "sixty", and the third is read as text.
Applying the same logic to your phone number.
(I'm not talking about actual implementation, just reasoning.)
If you read 1-1xx-2xx-9565 from the left to the right then the first distinct component is "1" which in it self is numerical and is read as "1". If the phone number would have started with "12-1xx" then the first component would have been read as "twelve" because its purely numerical.
The next component is "1xx" or "-1xx" depending on how you look at it. In either case it is a combination of numbers and letters, e.g. it is not purely numerical and is thus read out as text. If you include the "-" in that component is interpreted as a hyphen which isn't read out. That is why the the "-" is never read out for that component. The next component ("-2xx") is treated in the same way.
The final component is "-9565" which turns out to be a valid number. As seen in the cantaloupe sentence, VoiceOver reads this as a number in which case the "-" is no longer interpreted as a hyphen but as a "minus sign".
Getting VoiceOver to read your own text
On any label, view or other element in your application that is used with Voice Over, you can supply your own "accessibility label" when you know more about how you want the text to be read. This is done by simply assigning your own string to the accessibilityLabel property.
Now, you can create a suitable string in many different ways, a very simple one in your case would be to just add spaces everywhere so that each number is read individually. However, it seems a bit fragile to me, so I went ahead and used a number formatter to translate the individual numbers to their textual representations.
NSString *phoneNumber = #"1-1xx-2xx-9565";
// we want to know if a character is a number or not
NSCharacterSet *numberCharacters = [NSCharacterSet characterSetWithCharactersInString:#"0123456789"];
// we use this formatter to spell out individual numbers
NSNumberFormatter *spellOutSingleNumber = [NSNumberFormatter new];
spellOutSingleNumber.numberStyle = NSNumberFormatterSpellOutStyle;
NSMutableArray *spelledOutComonents = [NSMutableArray array];
// loop over the phone number add add the accessible variants to the array
[phoneNumber enumerateSubstringsInRange:NSMakeRange(0, phoneNumber.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
// check if it's a number
if ([substring rangeOfCharacterFromSet:numberCharacters].location != NSNotFound) {
// is a number
NSNumber *number = #([substring integerValue]);
[spelledOutComonents addObject:[spellOutSingleNumber stringFromNumber:number]];
} else {
// is not a number
[spelledOutComonents addObject:substring];
}
}];
// finally separate the components with spaces (so that the string doesn't become "ninefivesixfive".
NSString *yourAccessiblePhoneNumber = [spelledOutComonents componentsJoinedByString:#" "];
The result when I ran this was
one - one x x - two x x - nine five six five
If you need to do other modifications to your phone numbers to get them to read appropriately then you can do that. I suspect that you will use this is more than one place in your app so creating a custom NSFormatter might be a good idea.
Edit
On iOS 7 you can also use the UIAccessibilitySpeechAttributePunctuation attribute on an attributes string to change how it is pronounced.
Speech Attributes for Attributed Strings
Attributes that you can apply to text in an attributed string to modify how that text is pronounced.
UIAccessibilitySpeechAttributePunctuation
The value of this key is an NSNumber object that you should interpret as a Boolean value. When the value is YES, all punctuation in the text is spoken. You might use this for code or other text where the punctuation is relevant.
Available in iOS 7.0 and later.
Declared in UIAccessibilityConstants.h.
As of iOS 13 you can use a - NSAttributedString.Key.accessibilitySpeechSpellOut as a accessibilityAttributedLabel to make VoiceOver read each letter of the provided string (or a range of string).
So for example:
yourView.accessibilityAttributedLabel = NSAttributedString(string: yourText, attributes: [.accessibilitySpeechSpellOut: true])
If you want to spell all characters individually, a simple solution is to separate the characters by a comma ",".
You can use a String extension to convert the string:
extension String
{
/// Returns string suitable for accessibility (voice over). All characters will be spelled individually.
func stringForSpelling() -> String
{
return stringBySeparatingCharactersWithString(",")
}
/// Inserts a separator between all characters
func stringBySeparatingCharactersWithString(separator: String) -> String
{
var s = ""
// Separate all characters
let chars = self.characters.map({ String($0) })
// Append all characters one by one
for char in chars {
// If there is already a character, append separator before appending next character
if s.characters.count > 0 {
s += separator
}
// Append next character
s += char
}
return s
}
}
And then use it in the code like this:
myLabel.accessibilityLabel = myString.stringForSpelling()
Just Add a comma to each digit of the last number and after the last digit as well,. this will make sure voice over reads the last number as same as previous numbers.
example your number :- 1-1xx-2xx-9565
accessibility label :- 1-1xx-2xx-9,5,6,5,
Here is the code in Swift
public func retrieveAccessiblePhoneNumber(phoneNumber: String) -> String {
// We want to know if a character is a number or not
let characterSet = NSCharacterSet(charactersInString: "0123456789")
// We use this formatter to spell out individual numbers
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .SpellOutStyle
var spelledOutComponents = [String]()
let range = Range<String.Index>(start: phoneNumber.startIndex, end: phoneNumber.endIndex)
// Loop over the phone number add add the accessible variants to the array
phoneNumber.enumerateSubstringsInRange(range,
options: NSStringEnumerationOptions.ByComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) -> () in
// Check if it's a number
if let substr = substring where substr.rangeOfCharacterFromSet(characterSet) != nil {
if let number = Int(substr) {
// Is a number
let nsNumber = NSNumber(integer: number)
spelledOutComponents.append(numberFormatter.stringFromNumber(nsNumber)!)
}
} else {
// Is not a number
spelledOutComponents.append(substring!)
}
}
// Finally separate the components with spaces (so that the string doesn't become "ninefivesixfive".
return spelledOutComponents.joinWithSeparator(" ")
}

Need to check if NSArray object contains specific text. [duplicate]

This question already has answers here:
Finding a substring in a NSString object
(7 answers)
NSString search whole text for another string
(3 answers)
Closed 9 years ago.
I am performing a method call that places any objects found in the background into an NSArray object called "objects".
When I NSLog the counts property of "objects" it tells me that the array contains 1 object which is correct.
When I NSLog the NSArray object called "objects" it prints out the following:
(
"<PFUser:nzyjeVFgjU:(null)> {\n email = \"hahshshs#aol.com\";\n username = hzhjsshhsppppp;\n verificationCode = 6449;\n}"
)
Here is my problem. I need to create an if statement that takes another value I already have and compares it to the 4 digit number that verificationCode is equal to eg. Above in the code it says "verificationCode = 6449"
I am basically trying to compare a 4 digit code that I already have to the verificationCode that is contained in this single NSArray object.
I know how to write if statements, but i have no idea how to specifically focus on "verificationCode = 6449" since it is just text inside of a string.
I have been trying to figure out a way to do this for an hour now so any help is greatly appreciated thank you.
Just found the answer here: https://stackoverflow.com/a/7574136/3117509
I tried searching earlier but was searching for "search array for string" when I should have been searching for something like "search for string within a string" or "search for text within a string."
If I understand your question correctly, you're trying to iterate through an array and find if a string is there? Don't have access to a Mac right now, so I'm not sure if this will compile.
NSArray* someArray = [[NSArray alloc] initWithObjects:#"test", #"test1", #"test2", nil];
for(int i = 0; i < (int)[someArray count]; i++) {
if([[someArray objectAtIndex:i] isEqualToString #"test"]) {
//match found. handle the match
}
}

Resources