Custom font in a Cocoa application - objective-c

I know you can customize fonts by using Interface Builder and selecting a font. However, I'm curious if I can use a custom font that's not included by default on systems. Is there a way to include a custom font in my application?

While the manual font activation procedure is one option, you might also consider the ATSApplicationFontsPath Info.plist key:
Information Property List Key Reference:
"ATSApplicationFontsPath (String - Mac
OS X) identifies the location of a
font file or directory of fonts in the
bundle’s Resources directory. If
present, Mac OS X activates the fonts
at the specified path for use by the
bundled application. The fonts are
activated only for the bundled
application and not for the system as
a whole. The path itself should be
specified as a relative directory of
the bundle’s Resources directory. For
example, if a directory of fonts was
at the path
/Applications/MyApp.app/Contents/Resources/Stuff/MyFonts/,
you should specify the string
Stuff/MyFonts/ for the value of this
key."
I'd be sure to double-check, but I believe this functionality was added in OS X 10.5.x (which the code posted by Jinhyung Park targets).

ATSApplicationFontsPath uses [NSBundle mainbundle] path as base path, so it does not work when Resources folder is not located there (e.g. for app plugins).
In my Cocoa plugin I need to load custom fonts using CTFontManagerRegisterFontsForURL
#import <Cocoa/Cocoa.h>
static void FGDActivateFont(NSString *fontName)
{
// Can't make ATSApplicationFontsPath in Info.plist work for plugins (non-standard Resources path)
NSArray *availableFonts = [[NSFontManager sharedFontManager] availableFonts];
if (![availableFonts containsObject:fontName]) {
NSURL *fontURL = [[FGDRapidCart bundle] URLForResource:fontName withExtension:#"ttf" subdirectory:#"Fonts"];
assert(fontURL);
CFErrorRef error = NULL;
if (!CTFontManagerRegisterFontsForURL((__bridge CFURLRef)fontURL, kCTFontManagerScopeProcess, &error))
{
CFShow(error);
}
}
}
int main(int argc, char *argv[])
{
#autoreleasepool
{
FGDActivateFont(#“FontAwesome”);
}
return NSApplicationMain(argc, (const char **)argv);
}
Credits: https://github.com/OneSadCookie/Extendaword/blob/master/Extendaword/main.m

Here is the example for Mac App custom font loading.
NSString *fontFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"/fonts"];
fontsURL = [NSURL fileURLWithPath:fontFilePath];
if(fontsURL != nil)
{
OSStatus status;
FSRef fsRef;
CFURLGetFSRef((CFURLRef)fontsURL, &fsRef);
status = ATSFontActivateFromFileReference(&fsRef, kATSFontContextLocal, kATSFontFormatUnspecified,
NULL, kATSOptionFlagsDefault, NULL);
if (status != noErr)
{
errorMessage = #"Failed to acivate fonts!";
goto error;
}
}

I have managed to do this using cocos2d (CCLabelBMFont) and hiero tool. You will need to create the font using the hiero, and give this font to the CCLabelBMFont object.

Related

Not able to load a custom font into an OSX project but... am into an iOS project?

I am able to load a custom font in an iOS project and see it on the available fonts using:
NSArray *fontFamilies = [UIFont familyNames];
for (int i=0; i<[fontFamilies count]; i++)
{
NSLog(#"Font: %# ...", [fontFamilies objectAtIndex:i]);
}
But when I try and load the font into the OSX project, I am not able to see it in the list when I display the fonts:
NSLog(#"%#",[[[NSFontManager sharedFontManager] availableFontFamilies] description]);
I used: Unable to set custom font for the UILabel in XCode to install the font for iOS project and assumed it should be the same approach for the OSX project.
The link to the font file is in the bundle resources, Info.plist file(add: Fonts provided by application), and the file is in the Supporting files.
Am I missing something? thx
I solved it programmatically(but would prefer the approach like iOS approach in the link above):
NSString* path = [[NSBundle mainBundle] pathForResource:#"Symbola" ofType:#"ttf"];
NSURL* fontsURL = [NSURL fileURLWithPath:path];
FSRef fsRef;
CFURLGetFSRef((CFURLRef)fontsURL, &fsRef);
ATSFontActivateFromFileReference(&fsRef, kATSFontContextLocal, kATSFontFormatUnspecified,
NULL, kATSOptionFlagsDefault, NULL);

Stop copying file in Cocoa

I'm facing a problem I using the FSEvent to catch the file created in my app but I also want to check the file extension (ex: photo/ video type) are allowed, otherwise these files will not be copied. How can I check the file extension before copying in Cocoa? Any suggestion would be appreciated. Thanks
Please note I don't use NSFileManager to copy file.
Use following code to check for movies or audio file types. Full list of UTI-Types in the documentation.
NSString * fileUTI = nil;
BOOL success = [self getResourceValue:& fileUTI forKey:NSURLTypeIdentifierKey];
if (success && [uti isKindOfClass:[NSString class]]) {
BOOL fileConformsToUTI = UTTypeConformsTo(fileUTI, kUTTypeMovie);
fileConformsToUTI = fileConformsToUTI || UTTypeConformsTo(fileUTI, kUTTypeAudio);
// check and do the copy
}

Swift: cannot preload Coredata

When I include a SQLite file with Objective-C under "Target - Build Phases - Copy Bundle Resources", this file will be completely copied to the target, i.e. device or simulator. On the target, I get the whole file: tables and contents (Records/rows).
Doing the same with Swift, the tables will be copied, but they are empty: no records/rows. :-(
Can I do something additional? Is there any "trick"?
How can I preload my core data with base-records using Swift???
I'm using Xcode v6.1.1, with Beta 6.2 it is the same.
This is my solution (for sqlite-files). I don't know why it is so "extravagant" in Swift. Perhaps there's an easier way?
Many THX to pbasdf!!!
Important: beneath the *.sqlite-file you must add the *.sqlite-shm and the *.sqlite-wal to your project
This files will automatically be added to "Target - Build Phases - Copy Bundle Resources"
This is based on the Standard-Template for the "AppDelegate.swift"…
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let dataName = „MyData.sqlite" // must be replaced by the name of your file!
let modelName = „MyData“ // must be replaced by the name of your model!
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent(dataName)
let fileManager = NSFileManager.defaultManager()
let bundle = NSBundle.mainBundle()
let fromURL = bundle.URLForResource(modelName, withExtension: "sqlite")
// check sqlite-file: must it be installed?
if !fileManager.fileExistsAtPath(url.path!) {
self.copySqlliteFiles(modelName, databaseName: dataName)
}
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil {
...}
return coordinator
}()
// MARK: - copy sqllite-files (c) by Ray Wenderlich & Team in „“Core Data by Tutorials“
// check sqlite-files: they must be installed...
func copySqlliteFiles(modelName: String, databaseName: String)
{
let bundle = NSBundle.mainBundle()
let baseDatabaseURL = bundle.URLForResource(modelName, withExtension: "sqlite")
let documentsURL = self.applicationDocumentsDirectory
let storeURL = documentsURL.URLByAppendingPathComponent(databaseName)
NSFileManager.defaultManager().copyItemAtURL(baseDatabaseURL!, toURL: storeURL,error: nil)
let baseSHMURL = bundle.URLForResource(modelName,withExtension: "sqlite-shm")
let shmURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent(databaseName + "-shm")
NSFileManager.defaultManager().copyItemAtURL(baseSHMURL!, toURL: shmURL, error: nil)
let walURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent(databaseName + "-wal")
let baseWALURL = bundle.URLForResource(modelName, withExtension: "sqlite-wal")
NSFileManager.defaultManager().copyItemAtURL(baseWALURL!, toURL: walURL, error: nil)
}
I think this probably has to do with how files are laid out on iOS. Specifically:
when you copy the source file (by Xcode or at install time) it gets placed into the application bundle/container directory (which is read-only)
The standard CoreData initialization code looks for the data store in the Documents directory.
Not finding your source data (because it's in the wrong place), CoreData creates a new empty store.
To fix this, you need to copy the store from the Application directory to the Documents directory if it doesn't already exist.
This link gives an explanation and example code to do that. But in Objective-C it looks like:
NSString *storePath = [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"MyDB.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
// Copy the default db if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:#"MyDB" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath
toPath:storePath error:NULL];
}
}
I had a related issue related to this. I started working on some tutorials which used this line:
documentsURL.URLByAppendingPathComponent("StoreName")
At some point this stopped working. Data was not being written to the store anymore. To see why data was not being written, I tried opening the sqlite file but could not find it on my mac at the URL being used. The app folder was there, but there was no sqlite file. I looked back at older core data projects and found they used .sqlite extension. So I modified the line to:
documentsURL.URLByAppendingPathComponent("StoreName.sqlite")
This seemed to work so give it a try.

How do I get a document UTI from a file path?

I have a document path as an NSString. How do I get its UTI as an NSString? I currently use LSCopyItemAttribute but that requires an FSRef and all the functions for making an FSRef seems to be deprecated.
(Note: This is for Mac OS 10.8+.)
You will be able to get it using mobile core services framework. Refer the code below
NSString *path; // contains the file path
// Get the UTI from the file's extension:
CFStringRef pathExtension = (__bridge_retained CFStringRef)[path pathExtension];
CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, NULL);
CFRelease(pathExtension);
The code snippet is taken from here.
Use can use url.resourceValues(forKeys: (as mentioned in the comments):
if let resourceValues = try? url.resourceValues(forKeys: [.typeIdentifierKey]),
let uti = resourceValues.typeIdentifier {
return uti
}

Confused on creating new directories and changing directories (Objective-C)

When I changed the current directory path of main.m to newDir why does it still say that there are no files in newDir? Also I ran this program multiple times with no errors. Does that mean I ended up creating multiple newDir?
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSString *newDir = #"newDir";
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager createDirectoryAtPath:newDir withIntermediateDirectories:YES attributes:nil error:NULL] == NO) {
NSLog(#"couldnt create new directory");
return 1;
}
if ([manager changeCurrentDirectoryPath: newDir] == NO) {
NSLog(#"couldnt change directory path");
return 2;
}
NSLog(#"%#", [manager currentDirectoryPath]);
NSLog(#"%#", [manager contentsOfDirectoryAtPath:newDir error:NULL]);
}
return 0;
}
Output:
2012-08-07 10:27:20.428 Test[853:707] /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir
2012-08-07 10:36:47.832 Test[885:707] (null)
The path to main.m does not play into what happens when you run your program: the only question is whether the directory has any files or not, and from the log it appears that it doesn't.
To create some files in the directory, run these commands in the terminal window:
touch /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir/quick.txt
touch /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir/brown.txt
touch /Users/ss/Library/Developer/Xcode/DerivedData/Test-bfrqtnrhaafmdzghoyirjnfqjbfc/Build/Products/Debug/newDir/fox.txt
This will create three empty files. Now run your program, and see if it discovers the newly created txt files; it should.
On your second question, the operating system would not let you create multiple file system objects with identical names, so the answer is no, you created only one newDir.
Since you're ignoring errors on your contentsOfDirectoryAtPath call, it's entirely possible that the call is failing -- hence the null.
Without looking at references, it appears that you're looking for directory .../newDir/newDir, a directory that likely does not exist.
In any event, since newDir is new it wouldn't contain any entries (other than . and ..).