Zero Width Space in Swift? - ios

How to create NSString of zero width space.
In Objective-C, I used to create like this:
NSString *emptyText = #"\u200B";
How to create it in Swift? I have tried following, but it is not working.
let emptyString = "\u200B"

Use this syntax in Swift
let emptyString = "\u{200B}"
Of course you can also use unicode characters directly in your Swift code.

Related

How to find and replace UILabel's value on a specific line?

I know I can find the total number of lines of a UILabel with .numberOfLines but how can I retrieve and edit the value say on line 2.
Example:
Assuming from your screen shot that each line is separated by a newline character you can split the text based on that.
Here is an example in Swift 3:
if let components = label.text?.components(separatedBy: "\n"), components.count > 1 {
let secondLine = components[2]
let editedSecondLine = secondLine + "edited"
label.text = label.text?.replacingOccurrences(of: secondLine, with: editedSecondLine)
}
You should make sure there is a value at whatever index your interested in. This example makes sure that there are more than a single component before retrieving the value.
You can then replace the second line with your edited line.
Hope that helps.

Replace iOS app emoji with twitter open source twemoji

I want to replace all standard iOS emoji from a UILable or UITextView with twitters open source twemoji.
I can't find any library or documentation to do this in iOS. Does anyone have a solution that does not involve me implementing this from scratch?
The solution needs to be efficient and work offline.
The question got me intrigued, and after a bit of searching on how it would be possible to replace all standard iOS emoji with a custom set, I noticed that even Twitter's own iOS app doesn't use Twemoji:
In the end, I came to the same conclusion as you:
I can't find any library or documentation to do this in iOS.
So, I created a framework in Swift for this exact purpose.
It does all the work for you, but if you want to implement your own solution, I'll describe below how to replace all standard emoji with Twemoji.
1. Document all characters that can be represented as emoji
There are 1126 base characters that have emoji representations, and over a thousand additional representations formed by sequences. Although most base characters are confined to six Unicode blocks, all but one of these blocks are mixed with non-emoji characters and/or unassigned code points. The remaining base characters outside these blocks are scattered across various other blocks.
My implementation simply declares the UTF-32 code points for these characters, as the value property of UnicodeScalar is exactly this.
2. Check whether a character is an emoji
In Swift, a String contains a collection of Character objects, each of which represent a single extended grapheme cluster. An extended grapheme cluster is a sequence of Unicode scalars that together represent one1 human-readable character, which is helpful since you can loop through the Characters of a string and handling them based on the UnicodeScalars they contain (rather than looping through the UTF-16 values of the string).
To identify whether a Character is an emoji, only the first UnicodeScalar is significant, so comparing this value to your table of emoji characters is enough. However, I'd also recommend checking if the Character contains a Variation Selector, and if it does, make sure that it's VS16 – otherwise the character shouldn't be presented as emoji.
Extracting the UnicodeScalars from a Character requires a tiny hack:
let c: Character = "A"
let scalars = String(c).unicodeScalars
3. Convert the code points into the correct format
Twemoji images are named according to their corresponding code points2, which makes sense. So, the next step is to convert the Character into a string equivalent to the image name:
let codePoint = String("🙃").unicodeScalars.first!.value // 128579
let imageName = String(codePoint, radix: 16) // "1f643"
Great, but this won't work for flags or keycaps, so we'll have to modify our code to take those into account:
let scalars = String("🇧🇪").unicodeScalars
let filtered = scalars.filter{ $0.value != 0xfe0f } // Remove VS16 from variants, including keycaps.
let mapped = filtered.map{ String($0.value, radix: 16) }
let imageName = mapped.joined(separator: "-") // "1f1e7-1f1ea"
4. Replace the emoji in the string
In order to replace the emoji in a given String, we'll need to use NSMutableAttributedString for storing the original string, and replace the emoji with NSTextAttachment objects containing the corresponding Twemoji image.
let originalString = "🙃"
let attributedString = NSMutableAttributedString(string: originalString)
for character in originalString.characters {
// Check if character is emoji, see section 2.
...
// Get the image name from the character, see section 3.
let imageName = ...
// Safely unwrapping to make sure the image exists.
if let image = UIImage(named: imageName) {
let attachment = NSTextAttachment()
attachment.image = image
// Create an attributed string from the attachment.
let twemoji = NSAttributedString(attachment: attachment)
// Get the range of the character in attributedString.
let range = attributedString.mutableString.range(of: String(character))
// Replace the emoji with the corresponding Twemoji.
attributedString.replaceCharacters(in: range, with: twemoji)
}
}
To display the resulting attributed string, just set it as the attributedText property of a UITextView/UILabel.
Note that the above method doesn't take into account zero-width joiners or modifier sequences, but I feel like this answer is already too long as it stands.
1. There is a quirk with the Character type that interprets a sequence of joined regional indicator symbols as one object, despite containing a theoretically unlimited amount of Unicode scalars. Try "🇩🇰🇫🇮🇮🇸🇳🇴🇸🇪".characters.count in a playground.
2. The naming pattern varies slightly when it comes to zero-width joiners and variation selectors, so it's easier to strip these out of the image names – see here.
Easiest thing to do:
1) Load the twemoji images into your project.
2) Create an NSDictionary that correlates the emoji codes supported by iOS with the paths to the respective twemoji images:
NSArray *iOSEmojis = #[#"iOSEmoji1",#"iOSEmoji2];
NSDictionary *twemojiPaths = [NSDictionary dictionaryWithObjects:#[#"Project/twemoji1.png",#"Project/twemoji2.png"] andKeys:#[#"iOSEmoji1","iOSEmoji2"]];
3) Code your app to search for emoji strings and display the twemojis where the regular emojis would go:
for (NSString *emoji in iOSEmojis)
{
NSString *twemojiPath = [twemojiPaths valueForKey:emoji];
// Find the position of the emoji string in the text
// and put an image view there.
NSRange range = [label.text rangeOfString:emoji];
NSString *prefix = [label.text substringToIndex:range.location];
CGSize prefixSize = [prefix sizeWithAttributes: #{NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue" size:14]}];
CGSize emojiSize = [label.text sizeWithAttributes: #{NSFontAttributeName: [UIFont fontWithName:#"HelveticaNeue" size:14]}];
CGRect imageViewFrame = CGRectMake(prefixSize.width,label.frame.size.height,emojiSize.width,label.frame.size.height);
imageViewFrame = [self.view convertRect:imageViewFrame fromView:label];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:imageViewFrame];
imageView.image = [UIImage imageWithContentsOfFile:twemojiPath];
}

Cannot call value of non-function type double

I am quite new to programing in swift and I am working on a music app for iOS that adjusts the font size of the text in a UILabel in proportion to the string's length. In my code, I am trying to count the number of characters in the string statement and have it plugged into a formula, but for some reason Xcode gives me the error: Cannot call value of non function type double I tried setting the value to a CGFloat but it still gives me the same error on the "let b = 41.2 - .8(a) line. Thank you so much and sorry if this seems like a basic question.
let title = "Let It Bleed"
AlbumName.text = title
let a = title.characters.count
if ( a <= 19){
let b = 41.2 - .8(a)
let fontsize = CGFloat(b)
AlbumName.font = AlbumName.font.fontWithSize(fontsize)
}
A screenshot of the code with the error
I assume you expect "0.8 times a" with .8(a).
Three things:
You need leading 0 to represent fractional values in Swift.
You need explicit operator * for multiplication.
You need to convert numeric types to match for mathematical operations.
All these included, your line of interest becomes like this:
let b = 41.2 - 0.8 * CGFloat(a)

UITextView control line breaks

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)}

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