Category Archives: Development note

Tint transparent images on iOS

Recently, I encountered a problem that I need to change the color of non-transparent pixels in an image with a transparent background without affecting much the original quality (at least not draw with hard edges). Therefore I did some searches on Google and found the following solution (credit at the end). The following solution does not care about the luminosity of the image.

CGImageRef image;

CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, image);

or exchange the steps,

CGImageRef image;

CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, image);

CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);

For more details or other solutions, please refer to the link in credit below.
Credit: http://stackoverflow.com/questions/3514066/how-to-tint-a-transparent-png-image-in-iphone

Read Alarms in iOS

Notice: This method is undocumented and only for tweaks or apps that will not be submitted to AppStore.

This article will introduce a way to read (or add, but not included here) alarms in iOS. There is a private framework (MobileTimer.framework) originally provided for iOS stock app, Clock to manipulate clocks and system alarms.

Obviously, we will only use two classes when dealing with alarms, AlarmManager and Alarm. First, we have to get the singleton instance of AlarmManager.

AlarmManager *manager = [AlarmManager sharedManager];

Before accessing the alarms, it is required to load alarms first.

[manager loadAlarms];

Then, you can access the array containing all available alarms (Alarm) in Clock app.

NSArray *alarms = [manager alarms];

However, there is a problem in the framework when loading the alarms. For example, the  code is running in SpringBoard, while at the same time the alarms are being modified in stock Clock app. Even if you load alarms in SpringBoard again, the alarm data returned are still outdated. To fix this, hook a class method in AlarmManager to force synchronizing the preference values before the original method read from the preference.

%hook AlarmManager

+ (id)copyReadAlarmsFromPreferences {
	CFPreferencesAppSynchronize(CFSTR("com.apple.mobiletimer"));
	return %orig;
}

%end

Create note in official Notes app

20 May: Updated with iCloud synchronization support

Notice: This method is undocumented and only for tweaks or apps that will not be submitted to AppStore.

Lately, as I was developing Tap to Note, I had to find a way to add notes in official Notes app without directly updating notes database file (SQLite).

Eventually, I used the private framework (Notes.framework) to perform update. Here only covers the way to insert a new note.

The Notes framework uses Core Data to perform all changes to the note database file. For those who don’t know what Core Data is, check out the documentation here and example here.

The first step is to initialize a new note context (NoteContext) ourselves (not sure if this is the most correct way).

NoteContext *noteContext = [[NSClassFromString(@"NoteContext") alloc] init];

UPDATE: To support iCloud synchronization, add the following line.

[noteContext enableChangeLogging:YES];

Then get the NSManagedObjectContext (for Core Data) from the context instance.

NSManagedObjectContext *context = [noteContext managedObjectContext];

And the default note store object (NoteStoreObject) as well.

NoteStoreObject *store = [noteContext defaultStoreForNewNote];

Actually you may want to insert notes to other stores (e.g. local or iCloud). You can get the desired note store using other similar methods in NoteContext (e.g. allStoreslocalStore).

After that, we are going to create a note object as follows.

NoteObject *note = [NSClassFromString(@"NSEntityDescription") insertNewObjectForEntityForName:@"Note" inManagedObjectContext:context];
NoteBodyObject *body = [NSClassFromString(@"NSEntityDescription") insertNewObjectForEntityForName:@"NoteBody" inManagedObjectContext:context];

// set body parameters
body.content = @"note content";
body.owner = note; // reference to NoteObject

// set note parameters
note.store = store; // reference to NoteStoreObject
note.integerId = [noteContext nextIndex];
note.title = @"title here";
note.summary = @"summary here";
note.body = body; // reference to NoteBodyObject
note.creationDate = [NSData date];
note.modificationDate = [NSData date];

Note content is the full content with HTML code as the official app uses HTML tags to wrap lines (i.e. <div>, <br />). Consecutive spaces need to be replaced with “&nbsp;”. Everything will just be shown in a web view.

Title and summary are normally extracted from the first and second line of content respectively while summary can be nil or empty if there’s only one line of content.

The last step is, of course, to save the note and release the initialized note context instance.

NSError *error;

BOOL success = [noteContext saveOutsideApp:&error];
[noteContext release];

You could retrieve the error message from the error variable and get the insertion result (success or not) from the return value of the method saveOutsideApp:.