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
Related
Assuming we don't use ARC.
Suppose we have a very simple class in which we declare 2 NSString properties, like this :
#interface Foo : UIView {}
-(id)initWithArguments:(NSString*)mess title:(NSString*)tit;
#property(nonatomic, retain) NSString *message;
#property(nonatomic, retain) NSString *title;
#end
and in implementation :
#implementation Foo
#synthesize message, title;
-(id)initWithArguments:(NSString*)mess title:(NSString*)tit{
if((self = [super init])){
message = mess; // (1)
self.title = tit; // (2)
(...)
}
return self;
}
-(void)dealloc{
message = nil;
title = nil;
[super dealloc];
}
#end
Now if I call a method from another class, in which I create 2 NSString and an instance of Foo , like this :
-(void)someMethod{
NSString *string1 = [NSString stringWithFormat:#"some text with %d things", 5];
NSString *string2 = [NSString stringWithFormat:#"other text with %d things", 5];
Foo *foo = [[Foo alloc] initWithArguments:string1 title:string2];
}
The whole code works fine and doesn't crash, but, if I profile it with instruments,
it doesn't cause a leak when calling (1)("message = mess;")
it cause a leak when calling (2)("self.title = tit;")
It's very confusing, because stringWithFormat is an autoreleased object, isn't it ?
So, how an autoreleased object can cause a leak when assigning to a property ???
I read somewhere that it's almost always better to use the "self.text = value;" form instead of the "text = value;" form, because the second one may cause a leak.
Actually, in this code it's the contrary.
And... If I use a constant NSString like #"some text", instead of the values returned by [NSString stringWithFormat], there is no leak, of course.
Any idea ?
You have forgotten to invoke the (compiler-generated) setter methods in a few cases:
self.message = mess; // in init method
self.message = nil; // in dealloc method
self.title = nil; // ditto
It's crucial that you use the setter/getter methods in non-ARC code.
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.
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.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am a beginner in Objective-C. I would like to call the method two in file you.m from file me.m. Could you please teach me with simple example showing below to understand. Thank you!
you.h
#import <Foundation/Foundation.h>
#interface you : NSObject {
}
- (NSString *)one;
- (NSString *)two;
#end
you.m
#import "you.m"
#implementation you
- (NSString *)one {
NSString *a = #"this is a test.";
return a;
}
-(NSString *)two {
NSString *b = [self one];
return b;
}
#end
me.h
#import <Foundation/Foundation.h>
#interface me : NSObject {
}
#end
me.m
#import "you.h"
#import "me.h"
#implementation me
-(void)awakeFromNib{
//NSString *obj = [[[NSString alloc] init] autorelease];
//NSString *str = [obj two]; // dont work
//NSString *str = [self two]; // dont work
// I'd like to call method *two* from here.
NSLog(#"%#", str);
}
#end
In me class, create an instance of you.
you *objectYou=[you new];
As two returns a string, you need to store it :
NSString *string=[objectYou two];
In your code:
-(void)awakeFromNib{
you *objectYou=[you new];
NSString *str = [objectYou two];
NSLog(#"%#", str);
}
NOTE: Follow naming conventions. Class names must start with Capital letter like Me, You.
EDIT:
As you are learning, I would like to add one more thing, as you are calling one from two. If one is not meant to be called outside you class. You can define it in .m and remove the declaration from .h.
Simple, create an instance of You class in Me class and call that member function. Like so -
you *youInstance = [[you alloc] init];
NSString *retStr = [youInstance two];
Btw, its a good practice to CamelCase class names.
Also note this -
#interface you
- (NSString *) twoInstanceMethod;
+ (NSString *) twoClassMethod;
#end
NSString *retStr = [you twoClassMethod]; // This is ok
NSString *retStr = [you twoInstanceMethod]; // this doenst't work, you need an instance:
//so we create instance.
you *youInstance = [[you alloc] init];
NSString *retStr = [youInstance two];
Hope this clears some concepts...
.h
#class HtmlParser
#interface ClassName : NSObject <UITableViewDataSource>
{
NSString *img;
HtmlParser *htmlParser;
}
: )
.M
- (NSString*)img
{
if (img!=nil) return img;
if (_description!=nil)
{
// NSString* description = [NSString stringWithString:_description];
htmlParser = [[HtmlParser alloc] loadHtmlByString:(NSString*) _description];
}
return img;
}
I am trying to initialize HtmlParser with the contents of description. "description" is RSS html loaded asynchronously, started in the tableViewController.
I get a breakpoint with or without the NSString* description. '-[HtmlParser loadHtmlbyString:]: unrecognized selector sent to instance 0x75aa9b0'... That's all the debugging I know how to do. Breakpoints are enabled for all exceptions.
-the method in .m is called in the viewController's cellForRowAtIndexPath:
ClassName *object = _objects[indexPath.row];
NSString *i = object.img;
UIImage* iG = [UIImage imageWithData:
[NSData dataWithContentsOfURL:[NSURL URLWithString:i]]];
cell.imageView.image = iG;
Its messy so let me know if further clarification is needed.
.h
#interface HtmlParser: NSObject <NSXMLParserDelegate>
{
ET Cetera
}
- (id) loadHtmlByString:(NSString *)string;
When you call the method in question:
htmlParser = [[HtmlParser alloc] loadHtmlbyString:(NSString*) _description];
It shouldn't have the (NSString *) in there. It should be:
htmlParser = [[HtmlParser alloc] loadHtmlbyString: _description];
But, is loadHtmlbyString an init method? If so, then you should start the name with init, and you should also adhere to the naming conventions by capitalizing all the words in the name (including By).
The 'loadHtmlbyStringmethod is not a method of theHtmlParserclass, it is a method of yourClassName` class.
Don't you get a compiler warning on this line:
htmlParser = [[HtmlParser alloc] loadHtmlbyString:(NSString*) _description];
Look at the .h for the HtmlParser class and see what methods are defined for that class.