NSMutableArray UTF8 (Cyrillic, Chinese etc) store in NSString to email - ios

I have a
NSMutableArray *myArray;
and
#property (strong, nonatomic) NSMutableArray *myArray;
declared in the .h file.
In .m, myArray stores some (Cyrillic) Russian characters and shows up in the UITableView fine in Russian. HOwever, I want to email this, so I added
- (void)viewDidLoad
superArray=[[NSMutableArray alloc]init];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [superArray count];
}
and also this
NSString *emailString =[myArray description];
With the typical code:
NSString *messageBody = emailString;
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setMessageBody:messageBody isHTML:YES];
However, it's showing up with things like: \U0425\U044d etc etc
I figured since HTML is enabled I could add something like:
(EDITED HTML )
NSString *htmlportionBegin =#"<!DOCTYPE html> <html><head><meta charset='UTF-8'> </head><body>";
NSString *htmlportionEnd =#"</body></html>";
NSString *messageBody_Pre = [htmlportionBegin stringByAppendingString:emailString];
NSString *messageBody = [messageBody_Pre stringByAppendingString:htmlportionEnd];
But that doesn't change anything.
Anyone know how to get this to work so that the email composer in the app can read in the Russian characters?

Never use the description method to generate output to be seen by the user.
Instead, build your own string from the contents of the array. One possible way would be:
NSString *emailString = [myArray componentsJoinedByString:#", "]; // or use #"\n"
This will give you better output.
And if you state that the message body is HTML then you should have proper HTML tags in the message body including the html and body tags.

Related

Basic Plist help needed within XCode

I am very new to creating applications and haven't fully figured out how to use the plist function within XCode.
My problem is that I have 3 different input methods within a view controller to which the user will select values from, those being a stepper, a picker view and a date that logs the current date, which I would like to save to a plist so that the user can view those entries in a table view within another view controller.
I haven't really used a plist before therefore my question may sound very silly but regardless I need some help with this.
So far I have the inputs setup but they don't really do anything, I know this question is very basic but I am struggling to find information on this that doesn't go too technical.
I can post my code if that will be beneficial.
Any help will be greatly appreciated.
#property (weak, nonatomic) IBOutlet UILabel *balesFedLabel;
#property (weak, nonatomic) IBOutlet UIStepper *balesFedStepper;
#property (weak, nonatomic) IBOutlet UIPickerView *fieldPickerView;
#property (weak, nonatomic) IBOutlet UILabel *dateLabel;
#property (weak, nonatomic) IBOutlet UITextField *sheepGroup;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *backButton;
//Actions
- (IBAction)stepperValueChange:(id)sender;
- (IBAction)saveButton:(id)sender;
- (IBAction)textFieldDoneEditing:(id)sender;
#property NSArray *dataSource;
#property NSString *tempFieldSelection;
#property(nonatomic) UIKeyboardAppearance keyboardAppearanceDark;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setupArray];
NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"];
NSString *dateFormatString = [NSDateFormatter dateFormatFromTemplate:#"dd/MM/yyyy" options:0 locale:gbLocale];
NSLog(#"dataFormatString: %#", dateFormatString);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:dateFormatString];
NSString *stringFromDate = [dateFormatter stringFromDate:[NSDate date]];
self.dateLabel.text = stringFromDate;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {[self.view endEditing:YES];
}
- (void)setupArray {
_dataSource = [[NSArray alloc] initWithObjects:#"Cow Pasture", #"Top Lot", #"East Lot", #"West Lot", #"Front Meadow", #"Big Meadow", nil];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [_dataSource count];
}
- (UIView *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [_dataSource objectAtIndex:row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
self.tempFieldSelection = [_dataSource objectAtIndex:[pickerView selectedRowInComponent:0]];
}
- (IBAction)stepperValueChange:(id)sender {double baleStepperValue = self.balesFedStepper.value;
self.balesFedLabel.text = [NSString stringWithFormat:#"%.f", baleStepperValue];
}
- (IBAction)textFieldDoneEditing:(id)sender {[sender resignFirstResponder];
}
- (IBAction)saveButton:(id)sender {
NSLog(#"Bales Fed: %#", self.balesFedLabel.text);
NSLog(#"Sheep Group: %#", self.sheepGroup.text);
NSLog(#"Current Field: %#", self.tempFieldSelection);
NSLog(#"Last Date Fed: %#", self.dateLabel.text);
}
Use an NSMutableDictionary and pass it to the destination UIViewController in a property.
For example in your source view controller:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[#"Bales Fed"] = self.balesFedLabel.text;
dict[#"Sheep Group"] = self.sheepGroup.text;
dict[#"Current Field"] = self.tempFieldSelection;
dict[#"Last Date Fed"] = self.dateLabel.text;
Just pass dict to the destination view controller.
If you want to use a plist these are the two methods available in a NSDictionary class:
- writeToFile:atomically: to write the dictionary to a file (in plist format)
and the class method dictionaryWithContentsOfFile: or the instance method initWithContentsOfFile: to retrieve the dictionary from disk.
In your case, to write the dictionary to a file in plist format:
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"myData.plist"];
[dict writeToFile:filePath atomically:NO];
And in the destination view controller use this code to retrieve the plist from disk:
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"myData.plist"];
NSDictionary *myData = [NSDictionary dictionaryWithContentsOfFile:filePath];
From Apple's documentation for the first method:
This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
If the dictionary’s contents are all property list objects, the file written by this method can be used to initialize a new dictionary with the class method dictionaryWithContentsOfFile: or the instance method initWithContentsOfFile:.
I also recommend you to read the guide Property List Programming Guide
Even when i wouldn`t recommend to use a .plist file here is the code to use it:
NSString* plistPath = [[NSBundle mainBundle] pathForResource:#"YOUR_FILE_NAME" ofType:#"plist"];
NSDictionary *plistDic = [NSDictionary dictionaryWithContentsOfFile:plistPath];
As you are just passing small amounts of user entered data from one view controller to another you do not need to use PLists nor CoreData nor NSUserDefaults. These are all suitable for persistent data but from your description it sounds like it is just transient stuff.
Just store the user data in parameters and then pass them forward to the destination ViewController using the prepareForSegue method. See this SO answer for full details.

xCode won't allow addObject

absolute beginner here. I'm trying to teach myself Xcode using several different sources. My current lesson, I'm just trying to capitalize each word in a string. For some reason, I'm not given the option of using addObject, even though I've resorted to copying line for line what's in the book! Here's the code I'm using, I'm just typing it into the ViewController.m. I haven't touched the header file.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *myString = #"How much wood could a woodchuck chuck";
NSArray *wordsInSentence = [myString componentsSeparatedByString:#" "];
NSLog(#"%#", wordsInSentence);
NSMutableArray *capitalizedWords = [[NSMutableArray alloc] init];
for (int word =0; word < [wordsInSentence count]; word++)
{
NSString *uncapitalizedWords = [wordsInSentence objectAtIndex:word];
NSString *capitalizedWords = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWords];
}
NSLog(#"%#", capitalizedWords);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
My problem is with
[capitalizedWords addObject:capitalizedWords];
When I begin typing, it doesn't even show addObject in the drop down box as an option, the only option I have is addObserver.
Any and all help would be much appreciated, Thanks
The problem is that you have two variables with the same name, capitalizedWords. One is the mutable array, the other is the string. And, thus, when you use capitalizedWords inside that for loop, it's using the string rendition. I would suggest renaming the string variable, e.g., replace:
NSString *uncapitalizedWords = [wordsInSentence objectAtIndex:word];
NSString *capitalizedWords = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWords];
with
NSString *uncapitalizedWord = [wordsInSentence objectAtIndex:word]; // renaming this isn't critical, but using singular case makes it more clear
NSString *capitalizedWord = [uncapitalizedWord capitalizedString]; // renaming this fixes the problem
[capitalizedWords addObject:capitalizedWord];
The problem is that you are naming the string the same as the array.
Try changing
NSString *capitalizedWords = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWords];
to:
NSString *capitalizedWordsString = [uncapitalizedWords capitalizedString];
[capitalizedWords addObject:capitalizedWordsString];
When you're declaring the string with the same name as the array you're shadowing the array and Xcode will see capitalizedWords as a string, not an array. That's why it doesn't present you with the addObject method.

convert NSString into NSAttributedString without alloc init

I want to convert NSString into NSAttributedString.
But i always have to do
NSAttributedString *useDict1=[[NSAttributedString alloc] initWithString:#"String"];
Is there any other way such that i don't have to allocate the Dictionary every time, but just give the string?
I'd suggest to create a category on NSString with a method that converts it to NSAttributedString and then use that helper method across your project.
Like this:
#interface NSString (AttributedStringCreation)
- (NSAttributedString *)attributedString;
#end
#implementation NSString (AttributedStringCreation)
- (NSAttributedString *)attributedString {
return [[NSAttributedString alloc] initWithString:self];
}
#end

Reading and writing data to a property list

I know this question has been asked a hundred times here because I have been reading most of the questions about reading and writing to plists in this forum, in fact this is the main reason why I'm posting this question. I'm tired of trying outdated tutorials about this topic. I have tried a few tutorials but most of them are using .xib files or are not using ARC and I usually end up with a bunch of errors.
Does anyone know about a tutorial about reading/writing to a plist that uses storyboards and ARC? In other words an updated tutorial.
All I need is a tutorial that I can reference to have a better understanding on how to persist data using plists.
Thanks a lot
Here is a very simple piece of code that shows how to read and write to a plist. Code is based on this tutorial.
What I have here is basically two labels and two buttons on screen, one button to write the data and the other one is to read the data when clicked, the two labels are to show the first two items from an array (plist), item 0 will be shown in the first label and item 1 will be shown in the the second label.
.m file
-(NSString *)getFilePath
{
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathArray objectAtIndex:0] stringByAppendingPathComponent:#"fruitList.plist"];
}
-(void)saveData
{
NSArray *value = [[NSArray alloc] initWithObjects: #"Orange", #"Apple", nil];
[value writeToFile:[self getFilePath] atomically:YES];
}
-(void)loadData
{
NSString *myPath = [self getFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if(fileExists)
{
NSArray *values = [[NSArray alloc]initWithContentsOfFile:myPath];
self.labelOne.text = [values objectAtIndex:0];
self.labelTwo.text = [values objectAtIndex:1];
}
}
- (IBAction)writeData:(id)sender
{
[self saveData];
}
- (IBAction)readData:(id)sender
{
[self loadData];
}
.h file
#property (weak, nonatomic) IBOutlet UILabel *labelOne;
#property (weak, nonatomic) IBOutlet UILabel *labelTwo;
-(NSString*) getFilePath;
-(void) saveData;
-(void) loadData;
- (IBAction)writeData:(id)sender;
- (IBAction)readData:(id)sender;

Simple way to store NSMutableAttributedString in CoreData

I'm trying to store an NSMutableAttributedString in CoreData, but am running into problems since some of the attributes of my NSMutableAttributedString contain Core Foundation objects that can't be archived. Is there an easy way to get this object to store in CoreData without having to do some messy stuff manually?
NSMutableAttributedString conforms to NSCoding, which means that it knows how to convert itself to/from an NSData and does so via a protocol that Core Data knows how to use.
Make the attribute "transformable", and then just assign attributed strings to it. Since it's transformable, Core Data will use NSCoding to convert it to NSData when you assign a value, and to convert it back to an attributed string when you read it.
Note, you won't be able to use a predicate to filter results on this field. But storing and retrieving it is simple.
While the above answer is right, it has one big disadvantage:
It is not possible to build a fetch request / predicate that queries the content of the NSAttributedString object!
A predicate like this will cause an exception when executed:
[NSPredicate predicateWithFormat:#"(content CONTAINS[cd] %#)", #"test"]];
To store an 'fetchable' NSAttributedString in Core Data is is needed to spilt the NSAttributedString into two parts: A NSString side (which can be fetched) and a NSData side, which stores the attributes.
This split can be achieved by creating three attributes in the Core Data entity:
a shadow NSString attribute ('contentString')
a shadow NSData attribute ('contentAttributes')
an 'undefined' transient attributed ('content')
In the custom entities class the 'content' attributed the created from its shadows and changes to the attribute are also mirrored to its shadows.
Header file:
/**
MMTopic
*/
#interface MMTopic : _MMTopic {}
#property (strong, nonatomic) NSAttributedString* content;
#end
Implementation file:
/**
MMTopic (PrimitiveAccessors)
*/
#interface MMTopic (PrimitiveAccessors)
- (NSAttributedString *)primitiveContent;
- (void)setPrimitiveContent:(NSAttributedString *)pContent;
#end
/**
MMTopic
*/
#implementation MMTopic
static NSString const* kAttributesDictionaryKey = #"AttributesDictionary";
static NSString const* kAttributesRangeKey = #"AttributesRange";
/*
awakeFromFetch
*/
- (void)awakeFromFetch {
[super awakeFromFetch];
// Build 'content' from its shadows 'contentString' and 'contentAttributes'
NSString* string = self.contentString;
NSMutableAttributedString* content = [[NSMutableAttributedString alloc] initWithString:string];
NSData* attributesData = self.contentAttributes;
NSArray* attributesArray = nil;
if (attributesData) {
NSKeyedUnarchiver* decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:attributesData];
attributesArray = [[NSArray alloc] initWithCoder:decoder];
}
if ((content) &&
(attributesArray.count)) {
for (NSDictionary* attributesDictionary in attributesArray) {
//NSLog(#"%#: %#", NSStringFromRange(((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue), attributesDictionary[kAttributesDictionaryKey]);
[content addAttributes:attributesDictionary[kAttributesDictionaryKey]
range:((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue];
}
[self setPrimitiveContent:content];
}
}
/*
content
*/
#dynamic content;
/*
content (getter)
*/
- (NSAttributedString *)content {
[self willAccessValueForKey:#"content"];
NSAttributedString* content = [self primitiveContent];
[self didAccessValueForKey:#"content"];
return content;
}
/*
content (setter)
*/
- (void)setContent:(NSAttributedString *)pContent {
[self willChangeValueForKey:#"content"];
[self setPrimitiveValue:pContent forKey:#"content"];
[self didChangeValueForKey:#"content"];
// Update the shadows
// contentString
[self setValue:pContent.string
forKey:#"contentString"];
// contentAttributes
NSMutableData* data = [NSMutableData data];
NSKeyedArchiver* coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
NSMutableArray* attributesArray = [NSMutableArray array];
[pContent enumerateAttributesInRange:NSMakeRange(0, pContent.length)
options:0
usingBlock:^(NSDictionary* pAttributesDictionary, NSRange pRange, BOOL* prStop) {
//NSLog(#"%#: %#", NSStringFromRange(pRange), pAttributesDictionary);
[attributesArray addObject:#{
kAttributesDictionaryKey: pAttributesDictionary,
kAttributesRangeKey: [NSValue valueWithRange:pRange],
}];
}];
[attributesArray encodeWithCoder:coder];
[coder finishEncoding];
[self setValue:data
forKey:#"contentAttributes"];
}
#end
Fetching can now be done by:
[NSPredicate predicateWithFormat:#"(contentString CONTAINS[cd] %#)", #"test"]];
While any access to the NSAttributedString goes like this:
textField.attributedText = pTopic.content;
The rules for working with 'Non-Standard attributes' in Core Data are documented here: Apple docs
Well I am not sure what you are trying to do with the attributed string, but if it's formatted text then can't you use NSFont, etc..
Take a look here http://ossh.com.au/design-and-technology/software-development, I posted some stuff on formatting styles and images with uitextview and nstextview, but mostly it's about attributed strings.
This stuff is all stored in core data.
I started using CoreText when iOS5 was out, and thus used the Core Foundation values as attributes. However I now realize that since iOS6 came out, I can now use NSForegroundColorAttributeName, NSParagraphStyleAttributeName, NSFontAttributeName, etc. in the attributes dictionary, and those keys are accompanied by objects like UIColor, NSMutableParagraphStyle, and UIFont which can be archived.

Resources