Dynamically create NSString with Unicode emoji - ios

I have the string #"Hi there! \U0001F603", which correctly shows the emoji like Hi there! 😃 if I put it in a UILabel.
But I want to create it dynamically like [NSString stringWithFormat:#"Hi there! \U0001F60%ld", (long)arc4random_uniform(10)], but it doesn't even compile.
If I double the backslash, it shows the Unicode value literally like Hi there! \U0001F605.
How can I achieve this?

A step back, for a second: that number that you have, 1F660316, is a Unicode code point, which, to try to put it as simply as possible, is the index of this emoji in the list of all Unicode items. That's not the same thing as the bytes that the computer actually handles, which are the "encoded value" (technically, the code units.
When you write the literal #"\U0001F603" in your code, the compiler does the encoding for you, writing the necessary bytes.* If you don't have the literal at compile time, you must do the encoding yourself. That is, you must transform the code point into a set of bytes that represent it. For example, in the UTF-16 encoding that NSString uses internally, your code point is represented by the bytes ff fe 3d d8 03 de.
You can't, at run time, modify that literal and end up with the correct bytes, because the compiler has already done its work and gone to bed.
(You can read in depth about this stuff and how it pertains to NSString in an article by Ole Begemann at objc.io.)
Fortunately, one of the available encodings, UTF-32, represents code points directly: the value of the bytes is the same as the code point's. In other words, if you assign your code point number to a 32-bit unsigned integer, you've got proper UTF-32-encoded data.
That leads us to the process you need:
// Encoded start point
uint32_t base_point_UTF32 = 0x1F600;
// Generate random point
uint32_t offset = arc4random_uniform(10);
uint32_t new_point = base_point_UTF32 + offset;
// Read the four bytes into NSString, interpreted as UTF-32LE.
// Intel machines and iOS on ARM are little endian; others byte swap/change
// encoding as necessary.
NSString * emoji = [[NSString alloc] initWithBytes:&new_point
length:4
encoding:NSUTF32LittleEndianStringEncoding];
(N.B. that this may not work as expected for an arbitrary code point; not all code points are valid.)
*Note, it does the same thing for "normal" strings like #"b", as well.

\U0001F603 is a literal which is evaluated at compile time. You want a solution which can be executed at runtime.
So you want to have a string with a dynamic unicode character. %C if the format specifier for a unicode character (unichar).
[NSString stringWithFormat:#"Hi there! %C", (unichar)(0x01F600 + arc4random_uniform(10))];
unichar is too small for emoji. Thanks #JoshCaswell for correcting me.
Update: a working answer
#JoshCaswell has the correct answer with -initWithBytes:length:encoding:, but I think I can write a better wrapper.
Create a function to do all the work.
Use network ordering for a standard byte order.
No magic number for the length.
Here is my answer
NSString *MyStringFromUnicodeCharacter(uint32_t character) {
uint32_t bytes = htonl(character); // Convert the character to a known ordering
return [[NSString alloc] initWithBytes:&bytes length:sizeof(uint32_t) encoding:NSUTF32StringEncoding];
}
So, in use…
NSString *emoji = MyStringFromUnicodeCharacter(0x01F600 + arc4random_uniform(10));
NSString *message = [NSString stringWithFormat:#"Hi there! %#", emoji];
Update 2
Finally, put in a category to make it real Objective-C.
#interface NSString (MyString)
+ (instancetype)stringWithUnicodeCharacter:(uint32_t)character;
#end
#implementation NSString (MyString)
+ (instancetype)stringWithUnicodeCharacter:(uint32_t)character {
uint32_t bytes = htonl(character); // Convert the character to a known ordering
return [[NSString alloc] initWithBytes:&bytes length:sizeof(uint32_t) encoding:NSUTF32StringEncoding];
}
#end
And again, in use…
NSString *emoji = [NSString stringWithUnicodeCharacter:0x01F600 + arc4random_uniform(10)];
NSString *message = [NSString stringWithFormat:#"Hi there! %#", emoji];

Related

How to check how much two strings are different

How to compare two strings while some parts of them are same?
Let say I have a string ABCAAAA.
For some reason, only ONE character of the string ABCAAAA can be changed at a time. For example, I can change this string to DBCAAAA.
Now the problem is :
How can I ensure ONLY ONE character is changed each time? Is there a method for NSString to compare how much two strings differ?
Purpose: I put each string into own UITextField to determine whether this one is editable if others had changed. I need to ensure only one is edited at a time. So if one had been edited, I will set UITextField's enable to NO to disable editing.
There is no built-in NSString method available to do what you want. You need to write your own method. Objective-C does let you "extend" classes with new methods to cover cases like this.
This is how I would do it:
#interface NSString(Extend)
-(NSInteger)proximity:(NSString*)otherString;
#end
#implementation NSString(Extend)
-(NSInteger)proximity:(NSString*)otherString
{
NSUInteger length = [otherString length];
if(length != [self length]) return -1;
NSUInteger k;
NSUInteger differences = 0;
for(k=0;k<length;++k)
{
unichar c1 = [self characterAtIndex:k];
unichar c2 = [otherString characterAtIndex:k];
if(c1!=c2)
{
++differences;
}
}
return differences;
}
#end
Then in my code at the place I wanted to check I would say something like
Michael L gave a good answer (+1)
I just wanted to note that if all your text strings are in separate UITextFields, then only one of them can be edited at a time. Therefore I really don't understand what you want to do with enable = NO part.
If text strings must be edited in certain order, just keep count of order by index by yourself.

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

Is this the standard way to get text from label and concatenate with string in iOS?

Please bear with me as I'm very new to the world of iOS and Objective-C. I've read Apple's Obj-C primer, as well as a few free ones provided on the web.
On a button press, I'm trying to simply take the text of a label and concatenate it with a string. My mindset is still very much in Android/Java and how simple it could be, but I'm having trouble here. Nonetheless here is my code:
- (IBAction)myButton:(UIButton *)sender {
self.myLabel.text = [self.myLabel.text stringByAppendingString:#"obj-c is hard =/"];
}
It seems pretty standard, but I can imagine myself doing this often so I want to make sure this is correct or what other ways are there to do this?
Yes this is correct way. And if you want to use another method then use this one
self.myLabel.text = [NSString stringWithFormat:#"%# obj-c is hard =/",self.myLabel.text];
It is the standard way to join string.As ios updated syntaxes to make it easy like NSArray and NSDictiornary delaration but for concatenation it has not declared any shortcut way.
Have a look at this
OR
you can use a trick to simplify concatenation of string.Pass a parameter to macro and use following joining literal syntax.
#define STRING(text) #""text""
#implementation SPTViewController
- (void)viewDidLoad
{
NSString *joinedFromLiterals =STRING(#"Congratulations!, ") #"I " #"really " #"enjoy " #"carpenting!";
NSLog(#"joined string %#",joinedFromLiterals);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
output is ---------
joined string Congratulations!, I really enjoy carpenting!
Yes, this is correct, but there is a gotcha. If you haven't previously set the value of self.myLabel.text, it will be nil by default. Then the result of calling any method (like [self.myLabel.text stringByAppendingString:#"obj-c is hard =/"]) will also be nil, so myLabel will still have empty text. The way Objective-C handlesnil values is different than handling null in Java.
So to be safe, initialize label's text first:
self.myLabel.text = #"";
You are doing it right. Sure Objective-C is a bit more verbose than C# or Java or even Visual Basic .net (as I used to work on all those languages) but don't be bugged by those long method names. Although some #defines can be very helpful like (rewritten as C inline function):
static inline __attribute__((always_inline))
__attribute__((format(NSStirng, 1, 2)) NSString *SKSTR(NSString *fmt, ...)
{
va_list args;
va_start(args, fmt);
NSString *string = [[NSString alloc] initWithFormat:fmt arguments:args];
va_end(args);
#if !__has_feature(objc_arc)
[string autorelease];
#endif
return string;
}
Hope the __attribute__s and #ifs does not confuse you - you can safely ignore them.
To use:
self.label.text = SKSTR(#"%#, ugh!", self.label.text); // just like NSLog or snprintf :)

Format for int since XCode 4.5

Since upgrading to XCode 4.5, printing ints to the console results in unusually high values. Eg:
int someInt = 300;
NSLog([NSString stringWithFormat:#"Some int: %d", someInt]); // prints Some int: 11581443
Usually I only see this when using the wrong format string for the data type. I'm using LLDB.
you wrong use NSLog.
void NSLog (
NSString *format,
...
);
ex:
int someInt = 100;
NSString* str = [NSString stringWithFormat:#"%d",someInt];
NSLog(#"%#",str);
or
NSLog(#"%d", someInt)
or
NSLog(#"%#", [NSString stringWithFormat:#"%d",someInt])
Try NSLog(#"Integer: %i", int)
#askovpen is right about your incorrect use of NSLog, however this line in your question is interesting :
using the wrong format string for the data type
Of course you get garbage out - you're putting garbage in!
NSLog works by using the first parameter to work out how big the other parameters are going to be. i.e. if you put %c it expects a char next in the parameters. If you put %d it expects an int. So if you pass in an int and tell it to expect a float then yea, it's not going to work. Why would you expect that it would?
The reason you might be getting different values in XCode 4.5 instead of other XCodes might be due to changes in the memory management during compilation, or might be due to any number of other things.

NSString to float...what happens if NSString is not numerical and contains alphanumeric characters?

if
NSString sample = #"1sa34hjh##";
Float 64 floatsample = [sample floatValue];
what happens? what does floatsample contain?
Read the documentation.
Return Value
The floating-point value of the receiver’s text as a float, skipping whitespace at the beginning of the string. Returns HUGE_VAL or –HUGE_VAL on overflow, 0.0 on underflow.
Also returns 0.0 if the receiver doesn’t begin with a valid text representation of a floating-point number.
The best way to figure out the return value is to check the return value yourself. You can create a small program and save it as a file with a .m extension. Here's a sample:
// floatTest.m
#import <Foundation/Foundation.h>
int main() {
NSString *sample = #"1sa34hjh##";
float floatsample = [sample floatValue];
printf("%f", floatsample);
return 0;
}
Compile it on the command-line using clang and linking with the Foundation framework.
clang floatTest.m -framework foundation -o floatTest
Then run the executable and see the output.
./floatTest
The printed value is 1.000000. So to answer your question, if the string starts with a number, then the number portion of the string will be taken and converted to float. Same rules as above apply on overflow or underflow.
If creating the files seems like a hassle, you might like this blog post on minimalist Cocoa programming.

Resources