I am using Multiple custom fonts in my app.i know how to Add custom font but the Terrible parts of all this process is when we are going to use the custom font because mostly the custom font name is different from the file that we are using in app.here i would like to explain the recent example that i did,First of all i have added two custom font file in my app
For getting the above custom font names i tried this way
NSArray *familyNames = [[NSArray alloc] initWithArray:[UIFont familyNames]];
NSArray *fontNames;
NSInteger indFamily, indFont;
FontnameArray=[[NSMutableArray alloc] init];
for (indFamily=0; indFamily<[familyNames count]; ++indFamily)
{
fontNames = [[NSArray alloc] initWithArray:
[UIFont fontNamesForFamilyName:
[familyNames objectAtIndex:indFamily]]];
for (indFont=0; indFont<[fontNames count]; ++indFont)
{
NSString *fullName = [NSString stringWithFormat:#"%#",[fontNames objectAtIndex:indFont]];
[FontnameArray addObject:fullName];
}
}
[FontnameArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSLog(#" Font name: %#", FontnameArray);
The above Code display 199 font names for me ,so it was to difficult for me to pick the custom font name among 199 fonts, To make my doubt clear then i have simply drag the font file to font book Then i got the orignal names of custom Font.
[UIFont fontWithName:#"Ethiopia Primary" size:20]];
[UIFont fontWithName:#"Ethiopia jiret" size:20]];
So the Question is how can i get all Custom font names in Array like Above instead of draging the each font file to Fontbook for getting orignal names.
Just compare with all fonts exist in iOS
*Create new project like this code and write array of all font names to a plist file
- (void)writeAlliOSFontsToPlist{
NSArray *fontFamilies = [UIFont familyNames];
[self savePlist:#"AllFont" fromArray:fontFamilies];
}
- (NSString *) getFilePathForPlist:(NSString *) plistName {
// placeholder
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathArray objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.plist",plistName]];
}
-(void) savePlist:(NSString *)plistName fromArray:(NSArray *)array {
[array writeToFile:[self getFilePathForPlist:plistName] atomically:YES];
}
Open your simulator dir and copy this plist to your App bundle.
Then read this plist to an array and compare with all of your fonts by this code
-(void)dumpCustomFonts{
NSArray *iOSFontsArray = [NSArray arrayWithContentsOfFile:
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:
#"AllFont.plist"]];
NSMutableArray *fontFamilies = (NSMutableArray *)[UIFont familyNames];
[fontFamilies removeObjectsInArray:iOSFontsArray];
NSLog(#"%#",fontFamilies);
}
I've done all in my test project, you just copy and paste it.
You should add your custom font in .plist by
1) Add a new entry with the key "Fonts provided by application".
2) Now add each custom font file name with extension to this array like :
Now you can retrive all your custom fonts by :
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"Info.plist"];
NSDictionary *plistData = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
fontFamilyNames = [plistData valueForKey:#"UIAppFonts"];
NSLog(#"%#",fontFamilyNames);
NSString *fontFamilyName = [[fontFamilyNames objectAtIndex:index] stringByDeletingPathExtension];
UILabel *fontNameLabel = [[[UILabel alloc] init] autorelease];
fontNameLabel.text = #"Preview Text";
fontNameLabel.font = [UIFont fontWithName:fontFamilyName size:20.0];
Related
I am building an app for iPad and would like to use an already installed custom font in my ipad. I have downloaded a custom font (Cabin Sketch) from Adobe Creative Cloud Suite. After installing, I am able to see the font in General > Fonts
In my code, I am trying to print all the available fonts in the system but unable to see the one that's already been installed.
Here are the codes I have tried to print the font families.
NSDictionary *descriptorOptions = #{(id)kCTFontDownloadedAttribute : #YES}; // tried kCTFontDownloadableAttribute too
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes((CFDictionaryRef)descriptorOptions);
CFArrayRef fontDescriptors = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, NULL);
for(UIFontDescriptor *descriptor in (NSArray *)CFBridgingRelease(fontDescriptors)) {
NSString *fontFamilyName = [descriptor objectForKey:UIFontDescriptorFamilyAttribute];
NSLog(#"Font Family Name = %#", fontFamilyName);
}
NSArray *familyNames = [UIFont familyNames];
for (NSString *aFamilyName in familyNames) {
NSArray *fontNames = [UIFont fontNamesForFamilyName:aFamilyName];
for (NSString *aFontName in fontNames) {
NSLog(#"%#", aFontName);
}
}
QCFType<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames();
for (NSString *familyName in familyNames.as<const NSArray *>())
NSLog(#"%#", familyName);
Thanks in advance.
I have imported 2 font files to my resources group in my project, and also added them to my plist file:
pt-sans.narrow-bold.ttf and PT_Sans-Narrow-Web-Bold.ttf
I want to set bold font to attributed string, but it can't find my fonts, i am getting this error:
(UIFont*) nil
This is how i am setting the font:
UIFont *font_bold=[UIFont fontWithName:#"PT Sans Narrow Bold" size:14.0f];
It needs to be font from this family. Any idea, what am i doing wrong?
You can check the font's are accessible by adding this to your didFinishLaunchingWithOptions
NSArray *fontFamilies = [UIFont familyNames];
for (int i = 0; i < [fontFamilies count]; i++)
{
NSString *fontFamily = [fontFamilies objectAtIndex:i];
NSArray *fontNames = [UIFont fontNamesForFamilyName:[fontFamilies objectAtIndex:i]];
NSLog (#"%#: %#", fontFamily, fontNames);
}
and as #mikle94 said, caps is probably your issue.
In cases where we want to use fonts in our app, which don't already exist, we should pay attention to the several things, in order not to do the same mistake i did, and after that wonder "what is wrong, my code is perfectly written".
Copy the font in .otf or .ttf to your project.
Add it to the .plist file. Add 'Fonts provided by application' key in your plist file and in Item 0 copy the font name, which you already imported to your project.
Check in your Target -> Build Phases -> Copy Bundle Resources. If you don't see your font in there, add it.
Very Important - List the available fonts to check the correct names which you can use in your app:
NSArray *fontFamilies = [UIFont familyNames];
for (int i = 0; i < [fontFamilies count]; i++)
{
NSString *fontFamily = [fontFamilies objectAtIndex:i];
NSArray *fontNames = [UIFont fontNamesForFamilyName:[fontFamilies objectAtIndex:i]];
NSLog (#"%#: %#", fontFamily, fontNames);
}
Find the font you need and set it this way:
UIFont *customFont = [UIFont fontWithName:#"PTSans-NarrowBold" size:20];
I've already looked at Parse Plist (NSString) into NSDictionary and deemed it to be not a duplicate, as that question and its answer do not address my concerns.
I have a .plist file in the file system structured like this:
The source code of this .plist file looks like this:
{
"My App" = {
"Side Panel" = {
Items = {
Price = "#123ABC";
};
};
};
}
I know how to get an item in the Root like this:
[[NSBundle mainBundle] pathForResource:#"filename" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSString value = [dict objectForKey:#"key"]);
But what if the structure is like mine, with tiered dictionaries? How do I get the value of Price?
I would like to do this all in one method, ideally like this:
Calling
NSString *hexString = [self getColorForKey:#"My App.Side Panel.Items.Price"];
Definition
- (NSString *) getColorForKey: (NSString *)key
{
NSArray *path = [key componentsSeparatedByString:#"."];
NSDictionary *colors = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Colors" ofType:#"plist"]];
NSString *color = #"#FFFFFF"; // white is our backup
// What do I put here to get the color?
return color;
}
Here's the solution that worked for me:
+ (NSString*) getHexColorForKey:(NSString*)key
{
NSArray *path = [key componentsSeparatedByString:#"."];
NSDictionary *colors = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Colors" ofType:#"plist"]];
NSString *color = #"#FFFFFF";
for (NSString *location in path) {
NSObject *subdict = colors[location];
if ([subdict isKindOfClass:[NSString class]])
{
color = (NSString*)subdict;
break;
}
else if ([subdict isKindOfClass:[NSDictionary class]])
{
colors = (NSDictionary*)subdict; // if it's a dictinoary, our color may be inside it
}
else
{
[SilverLog level:SilverLogLevelError message:#"Unexpected type of dictinoary entry: %#", [subdict class]];
return color;
}
}
return color;
}
where key is an NSString that matches /^[^.]+(\.[^.]+)*$/, meaning it looks like my targeted #"My App.Side Panel.Items.Price".
Yes I understand what you're looking to accomplish; thank you for the clarification. I will however add that the resources and advice I have written do provide the necessary information resolve your problem.
That said, the following gets your dictionary:
NSURL *plistURL = [[NSBundle mainBundle] URLForResource:#"Info" withExtension:#"plist"];
NSData *plistData = [NSData dataWithContentsOfURL:plistURL];
NSDictionary *tieredPlistData = [NSPropertyListSerialization propertyListWithData:plistData
options:kCFPropertyListImmutable
format:NULL
error:nil];
Then, if we're interested in the information contained in Items
NSDictionary *allItemsDictionary = tieredPlistData[#"My App"][#"Side Panel"][#"Items"];
Assuming that Items will contain a number of objects, you could use
NSArray *keys = [allItems allKeys];
for(NSString *key in keys){
NSString *colorValue = allItemsDictionary[key];
// do something with said color value and key
}
Or, if there is a single value you need, then just reference that key
NSString *colorForPriceText = allItemsDictionary[#"Price"];
But a few tips:
It's generally considered a better idea to keep frequently accessed values in code instead of a plist/file that is loaded at runtime.
That said, you wouldn't put your call to load from NSBundle in the same method you would use to query a specific value. In your example, every time you need a color, you end up re-accessing NSBundle and pile on unneeded memory allocations. One method would load the plist into an iVar NSDictionary and then that NSDictionary would be used separately by another method.
I have a custom built font that I've added to the Xcode project. I've followed all the instructed steps in adding the font and when I set a UILabel or UITextField as the custom font in IB it works great, however when I set the custom font at runtime it appears as the iOS default font instead. I've checked the UIAppFonts list and have them print out with NSLog where it shows the full name so I know that when I call [UIFont fontWithName:#"font name" size:s] it's the correct name. What could be the issue?
The font name finding method below (This method provided the incorrect names to use in custom UIFont instantiation):
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
NSArray* fontFiles = [infoDict objectForKey:#"UIAppFonts"];
for (NSString *fontFile in fontFiles) {
NSURL *url = [[NSBundle mainBundle] URLForResource:fontFile withExtension:NULL];
NSData *fontData = [NSData dataWithContentsOfURL:url];
CGDataProviderRef fontDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData);
CGFontRef loadedFont = CGFontCreateWithDataProvider(fontDataProvider);
NSString *fullName = CFBridgingRelease(CGFontCopyFullName(loadedFont));
CGFontRelease(loadedFont);
CGDataProviderRelease(fontDataProvider);
NSLog(#"FULL NAME:%#",fullName);
}
Try executing this code:
for (NSString* family in [UIFont familyNames]) {
NSLog(#"%#", family);
for (NSString* name in [UIFont fontNamesForFamilyName: family]) {
NSLog(#" %#", name);
}
}
Do you see the font name you are trying to use?
I am using this code to add content into a plist :
//////////// Save data into plist /////////////////
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"datatesting.plist"];
NSLog(#"path='%#'",path);
NSFileManager *nfm = [[NSFileManager alloc] init];
if([nfm fileExistsAtPath:path])
{
// if file exists, get its contents, add more entries and write back
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSArray *keys = [NSArray arrayWithObjects:#"Title",#"Description",#"Coordinate",nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",titlestring],[NSString stringWithFormat:#"%#",descriptionstring],[NSString stringWithFormat:#"%#",coordinadastring], nil] forKeys:keys]];
NSLog(#"modified array=%#",array);
BOOL ok = [array writeToFile:path atomically:YES];
if(!ok){
NSLog(#"Unable to write appended file");
return;
}
} else {
// if file doesn't exist, create a new one
NSMutableArray *array = [[NSMutableArray alloc] init];
NSArray *keys = [NSArray arrayWithObjects:#"Title",#"Description",#"Coordinate",nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",titlestring],[NSString stringWithFormat:#"%#",descriptionstring],[NSString stringWithFormat:#"%#",coordinadastring], nil] forKeys:keys]];
NSLog(#"new array=%#",array);
BOOL ok = [array writeToFile:path atomically:YES];
if(!ok){
NSLog(#"Unable to write new file");
return;
}
}
Now I am having issues in using the content of the plist. So my two questions are :
- What is the Keys of the dictionaries of the current plist ?
- What is the way to read the content in a Tableview ?
You have an array of dictionaries. You need to iterate on the contents of the array, then look at each dictionary. You can do this with block enumerators or the older for() style. I'll use the latter now as you may not know blocks.
NSArray *array = ...; // read in the array from the file
for(NSDictionary *dict in array) {
NSString *title = [dict objectForKey:#"Title"];
... etc
}
When you want to show this data in a tablview, the number of rows is the number of dictionaries (ie [array count]). If you wanted to show just the title for each cell, you would get the cell row number, then get the dictionary by [array objectAtIndex:cellRow]), then pull out the title as above.