UITextView control line breaks - ios

I have a UITextView with items looking like this:
artist1 - song name1, artist2 - song name 2,
artist3 - song name3, etc.
I only want to disallow line breaks except between artists (after comma, before next artist), so this would be invalid:
artist 1 - song name1, artist 2 - // <- invalid break
song name 2.
I have tried two approaches for accomplishing this: Firstly, I put things in a webview, making this trivial to solve. Unfortunately, that is not possible for a couple of other reasons.
Secondly, I added _ where there are white spaces and hid them, like this:
artist_1_-_song_name1, artist_2_-_song_name_2, etc // <- setting colour of _ to invisible.
This almost worked, but now it breaks at the hyphen, like this:
artist_1_-
song_name1
I'm firstly looking for a good way to solve this. If that doesn't work, I would settle for a Unicode Char that looks like a dash but doesn't break (the U+2015 horizontal bar is too long)
Edit: Just found out about about Unicode Character 'NON-BREAKING HYPHEN' (U+2011), so while the problem is technically solved, I'm still looking for a less hackish way to do it though.

What you want is a non breaking space, or Unicode Character u+00a0.
NSString *nonbreakingspace = #"\u00A0";
NSString *nonbreakinghyphen = #"\u2011";
An replace your spaces in the artist1 - song name1 with the nonbreakingspace string and the hyphen with the non breaking hyphen nonbreakinghyphen string.
NSString *text = ...;
text = [text stringByReplacingOccurrencesOfString:#" " withString:nonbreakingspace];
text = [text stringByReplacingOccurrencesOfString:#"-" withString:nonbreakinghyphen];
myTextView.text = text;

swift 4 version!
var nonbreakinghyphen = "\u{2011}"
var text = "i-21"
myTextView.text = text.replacingOccurrences(of: "-", with: nonbreakinghyphen)}

Related

Stubborn emoji won't combine: 👨‍❤‍💋‍👨

Even after #user3441734 solved most of my problems 🙇, there are a few emoji that I can't seem to render properly when converting from a [String:String] to String.
Here's some Playground-ready code to illustrate the problem:
var u = ""
u = "1f468-1f468-1f467-1f467" // 👨‍👨‍👧‍👧
//u = "1f918-1f3ff" // 🤘🏿
//u = "1f468-2764-1f48b-1f468" // 👨‍❤‍💋‍👨 (broken)
//u = "1f3c7-1f3fb" // 🏇‍🏻 (broken)
let unicodeArray = u.characters.split("-")
.map(String.init)
.map {String(UnicodeScalar(Int($0,radix: 16) ?? 0))}
if let last = unicodeArray.last {
let separator: String
switch (unicodeArray.first, last) {
// Failed attempt to get tone applied to jockey
case let (horse_racing, _) where horse_racing == "\u{1f3c7}":
separator = "\u{200d}"
case let (_, tone) where "\u{1f3fb}"..."\u{1f3ff}" ~= tone:
separator = ""
case let (_, regionalIndicatorSymbol) where "\u{1f1e6}"..."\u{1f1ff}" ~= regionalIndicatorSymbol:
separator = ""
default:
separator = "\u{200d}"
}
print(unicodeArray.joinWithSeparator(separator))
}
Uncomment each assignment to u in turn to see the problem in action. The 3rd and 4th values should render like so:
and
Thoughts…
It turns out that a long-press on the race horse fails to show skin tones on iOS, so let's assume that's just an oversight, perhaps related to the near-impossibility of judging the jockey's skin tone at standard emoji sizes. I still can't figure out the problem with u = "1f468-2764-1f48b-1f468"
Apologies if this question comes out at all unclear. Chrome and Safari have different behaviors w.r.t these combo-emoji, so only the linked images are guaranteed to appear to you the way I see them on my end. 😢
These emoji are all either skin-tone renderings or tokens of same-sex affection. Is there some sort of bizarre latent racism & homophobia lurking in the system?! 😱 (Cue the conspiracy theories.)
Note that my attempt to use the zero-width joiner, u{200d} didn't help.
So, bug in Apple & Chrome's handling of certain emoji, or is there yet another idiosyncrasy of the standard that I've missed?
There's no conspiracy, the bugs are in your code.
The first character can be produced with:
U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468
Note the ZERO WIDTH JOINER (U+200D) between each character, and the VARIATION SELECTOR-16 selector (U+FE0F) on the HEAVY BLACK HEART (U+2764) to ensure the emoji presentation style is used.
Refer to this table for a complete list of implemented multi-person groupings.
U+1F3C7 HORSE RACING is not an emoji modifier base, and so it does not support skin tone modifiers.

Prevent UILabel from character wrapping symbols

I have a multi-line label that has the following text:
Lots of text here · $$$$
Since the text at the beginning is freeform, sometimes the wrapping ends up looking like this:
Lots of text here · $$$
$
How do I prevent this from happening? I want it to look like this:
Lots of text here ·
$$$$
I've tried every lineBreakMode to little avail. Word wrap doesn't work because it doesn't treat $$$$ as a word.
It seems that you might benefit from subclassing UILabel, which would treat a string differently for the NSLineBreakByWordWrapping line break mode, which treats your phonetic words like words. You will effectively be expanding the definition by which your custom linebreakmode considers a word.
You would have to roll your own line-breaking algorithm. The approach to determining the location of your line-breaks would be similar to the following:
Loop through the string, to get each character, until one of two conditions is met: a) you have reached the width of the view, or b) you have reached a space, and the next word (delimited by a space) doesn't fit on the same line.
If you have reached condition a, you have two options--you could either adopt a policy that never splits words into multiple lines, or your could only apply the non-split rule to your phonetic words. Either way, you will need to insert a line break at the beginning of the phonetic word, when there is no more room on a given line.
You may want to use two separate strings, to keep the source string separate from the display string that contains your formatting.
Let me know if that helps!
This might be very late but atleast it could help someone.
The way I have done it is as follows:
UIFont *fontUsed = [UIFont systemFontOfSize:17];
NSDictionary *dictFont = [NSDictionary dictionaryWithObject:fontUsed forKey:NSFontAttributeName];
NSString *strTextToShow = #"text that has to be displayed but without $$$$";
CGRect rectForSimpleText = [strTextToShow boundingRectWithSize:CGSizeMake(154, 258) options:NSStringDrawingUsesLineFragmentOrigin attributes:dictFont context:nil];
NSString *strTextAdded = [NSString stringWithFormat:#"%# $$$$", strTextToShow];
CGFloat oldHeight = rectForSimpleText.size.height;
CGRect rectForAppendedText = [strTextAdded boundingRectWithSize:CGSizeMake(154, 258) options:NSStringDrawingUsesLineFragmentOrigin attributes:dictFont context:nil];
CGFloat newHeight = rectForAppendedText.size.height;
if (oldHeight < newHeight) {
strTextAdded = [strTextAdded stringByReplacingOccurrencesOfString:#"$$$$" withString:#"\n$$$$"];
}
[lblLongText setText:strTextAdded];
lblLongText here is the IBOutlet of UILabel and CGSizeMake(154, 258) is the size of UILabel I have used. Let me know if there is any other way you have found.
Try inserting a line break in your input text.
Lots of text here ·\n $$$
It should print the $$$ in the next line.

Move Cursor One Word(including language like Chinese) at a Time in UTextView

I have read this,but it can only work well in English for it just use white-space and something like NewlineCharacterSet as separator.
I want to add a left arrow and a right arrow in the accessory input view to move the cursor in UITextView by words.
And I am wondering how to support that feature for some Asian languages like Chinese
PS:I will added an example that CFStringTokenizer failed to work with when there are both English Characters and Chinese characters
test string:
Happy Christmas! Text view test 云存储容器测试开心 yeap
the expected boundaries:
Happy/ Christmas!/ Text/ view/ test/ 云/存储/容器/测试/开心/ yeap/
the boundaries show in reality:
Happy/ Christmas!/ Text/ view/ test/ 云存储容器测试开心/ yeap/
I don't speak Chinese, but according to the documentation,
CFStringTokenizer is able to find word boundaries in many languages,
including Asian languages.
The following code shows how to advance from one word ("world" at position 6)
to the next word ("This" at position 13):
// Test string.
NSString *string = #"Hello world. This is great.";
// Create tokenizer
CFStringTokenizerRef tokenizer = CFStringTokenizerCreate(NULL,
(__bridge CFStringRef)(string),
CFRangeMake(0, [string length]),
kCFStringTokenizerUnitWordBoundary,
CFLocaleCopyCurrent());
// Start with a position that is inside the word "world".
CFIndex position = 6;
// Goto current token ("world")
CFStringTokenizerTokenType tokenType;
tokenType = CFStringTokenizerGoToTokenAtIndex(tokenizer, position);
if (tokenType != kCFStringTokenizerTokenNone) {
// Advance to next "normal" token:
tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer);
while (tokenType != kCFStringTokenizerTokenNone && tokenType != kCFStringTokenizerTokenNormal) {
tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer);
}
if (tokenType != kCFStringTokenizerTokenNone) {
// Get the location of next token in the string:
CFRange range = CFStringTokenizerGetCurrentTokenRange(tokenizer);
position = range.location;
NSLog(#"%ld", position);
// Output: 13 = position of the word "This"
}
}
There is no CFStringTokenizerAdvanceToPreviousToken() function, so to move to
the previous word you have to start at the beginning of the string and advance forward.
Finnally I use UITextInputTokenizer to realized the function

Which characters does NSLineBreakByWordWrapping break on?

Short version
From the docs:
NSLineBreakByWordWrapping
Wrapping occurs at word boundaries, unless the word itself doesn’t fit on a single line.
What is the set of all word boundary characters?
Longer version
I have a set of UILabels, which contain text, sometimes including URLs. I need to know the exact location (frame, not range) of the URLs so that I can make them tappable. My math mostly works, but I had to build in a test for certain characters:
// This code only reached if the URL is longer than the available width
NSString *theText = // A string containing an HTTP/HTTPS URL
NSCharacterSet *breakChars = [NSCharacterSet characterSetWithCharactersInString:#"?-"];
NSString *charsInRemaininsSpace = // NSString with remaining text on this line
NSUInteger breakIndex = NSNotFound;
if (charsInRemaininsSpace)
breakIndex = [charsInRemaininsSpace rangeOfCharacterFromSet:breakChars
options:NSBackwardsSearch].location;
if (breakIndex != NSNotFound && breakIndex != theText.length-1) {
// There is a breakable char in the middle, so draw a URL through that, then break
// ...
} else {
// There is no breakable char (or it's at the end), so start this word on a new line
// ...
}
The characters in my NSCharacterSet are just ? and -, which I discovered NSLineBreakByWordWrapping breaks on. It does not break on some other characters I see in URLs like % and =. Is there a complete list of characters I should be breaking on?
I recommend using "not alphanumeric" as your test.
[[NSCharacterSet alphanumerCharacterSet] invertedSet]
That said, if you're doing a lot of this, you may want to consider using CTFramesetter to do your layout instead of UILabel. Then you can use CTRunGetImageBounds to calculate actual rects. You wouldn't have to calculate your word breaks, since CTFrame will already have done this for you (and it would be guaranteed to be the same algorithm).

Character code in NSString to unicode character

I have an NSString with a charactercode like this: 0x1F514.
I want to take this NSString and add it to another NSString, but not with the literal value of it, but the icon hidden behind it. In this case an emoticon of a bell.
How can I easily convert this NSString to show the emoticon instead of the character code?
Something like this would do:
NSString *c = #"0x1F514";
unsigned intVal;
NSScanner *scanner = [NSScanner scannerWithString:c];
[scanner scanHexInt:&intVal];
NSString *str = nil;
if (intVal > 0xFFFF) {
unsigned remainder = intVal - 0x10000;
unsigned topTenBits = (remainder >> 10) & 0x3FF;
unsigned botTenBits = (remainder >> 0) & 0x3FF;
unichar hi = topTenBits + 0xD800;
unichar lo = botTenBits + 0xDC00;
unichar unicodeChars[2] = {hi, lo};
str = [NSString stringWithCharacters:unicodeChars length:2];
} else {
unichar lo = (unichar)(intVal & 0xFFFF);
str = [NSString stringWithCharacters:&lo length:1];
}
NSLog(#"str = %#", str);
The reason simply #"\u1f514" doesn't work is because those \u values cannot be outside the BMP, i.e. >0xFFFF, i.e. >16-bit.
So, what my code does is check for that scenario and does the relevant surrogate pair magic to make the right string.
Hopefully that is actually what you want and makes sense!
If your NSString contains this "bell" character, then it does. You just append strings the usual way, like with stringByAppendingString.
The drawing of a bell instead of something denoting an unknown character is a completely separate issue. Your best bet is to ensure you're not using CoreText for drawing this, as it's been reported elsewhere, and I've seen it myself at work, that various non-standard characters may not work when printed that way. They do work, however, when printed with UIKit (that should be standard UI components, UIKitAdditions, and so on).
If using CoreText, you might get a bit lucky if you disable some text properties for the string with this special character, or choose appropriate font (but I won't help you here; we decided to leave the issue as Won't fix).
Having said that, the last time I was dealing with those was in pre-iOS 6 days...
Summary: your problem is not appending strings, but how you draw them.

Resources