Problems underlying the UIDickBar are analyzed and concluded in here: Problems and the Solution for UIDickBar.
On March 31, 2011, Twitter removed the UIDickBar from their iPhone client version 3.3.3.
#dickbar, “Twitter’s mainstream consumer client experience,” now available for your iOS apps too.

This project got lots of attentions since this morning (thanks, @justin), and here are some great retweets:
And UIDickBar becomes the #1 trending repos within 24 hours, also the #1 featured repos on github.com.
Thanks again for all the warm welcome!
For those who want to create custom notification view, any forms of design similar to this are really bad idea, really, no kidding.
Because notification is a kind of interruption, it breaks the continuity. Unlike UIAlertView, UIDickBar uses disrupting way to interrupt information, and users feel confused when dealing with two different information flows at the same time. If you want to know more details about problems underlying the UIDickBar, read this.
Please think for your users before using it.
You simply init UIDickBar with title, badge and an action block:
UIDickBar *dickBar = [[UIDickBar alloc] initWithDickTitle:@"#DickBar" dickBadge:@"Stupid" actionBlock:^{
// Anything you want to do after UIDickBar tapped
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://digdog.tumblr.com"]];
}];
[dickBar showInView:self.view];
[dickBar release];
Then you call -showInView: to display UIDickBar that originates from the specified view.
UIDickBar is available on github.
UIDickBar is released under MIT License.
Say you have an non-ascii NSString like this:
NSString *inputString = @"永和九年歲在癸丑暮春之初會於會稽山陰之蘭亭修禊事也群賢畢至少長咸集此地有崇山峻岭茂林修竹又有清流激湍映帶左右引以為流觴曲水列坐其次雖無絲竹管弦之盛一觴一詠亦足以暢敘幽情是日也天朗氣清惠風和暢仰觀宇宙之大俯察品類之盛所以遊目騁懷足以極視聽之娛信可樂也夫人之相與俯仰一世或取諸懷抱悟言一室之內或因寄所託放浪形骸之外雖趣舍萬殊靜躁不同當其欣於所遇暫得於己怏然自足不知老之將至及其所之既倦情隨事遷感慨系之矣向之所欣俛仰之間已為陳跡猶不能不以之興懷況修短隨化終期於盡古人云死生亦大矣豈不痛哉每覽昔人興感之由若合一契未嘗不臨文嗟悼不能喻之於懷固知一死生為虛誕齊彭殤為妄作後之視今亦猶今之視昔悲夫故列敘時人錄其所述雖世殊事異所以興懷其致一也後之覽者亦將有感於斯文";
And you want to convert the string into a collection structure that:
at the same time.
The code snippet below is the answer, by converting the string into a mutable NSDictionary with character as key and NSMutableIndexSet value that contains the index of the character, you have text counts and indexes in one pass.
NSUInteger inputLength = [inputString length];
NSMutableDictionary *convertedInputDictionary = [[NSMutableDictionary alloc] init];
NSUInteger codePointIndex = 0;
while (codePointIndex < inputLength) {
NSRange characterRange = [inputString rangeOfComposedCharacterSequenceAtIndex:codePointIndex];
NSString *keyName = [inputString substringWithRange:characterRange];
NSMutableIndexSet *inputIndexes = [convertedInputDictionary valueForKey:keyName];
if (inputIndexes) {
[inputIndexes addIndex:characterRange.location];
} else {
NSMutableIndexSet *newInputIndexes = [[NSMutableIndexSet alloc] init];
[newInputIndexes addIndex:characterRange.location];
[convertedInputDictionary setValue:newInputIndexes forKey:keyName];
[newInputIndexes release];
}
codePointIndex += characterRange.length;
}
[convertedInputDictionary release];
The result will look like this:
{
"\U4e00" = "<NSMutableIndexSet>[number of indexes: 7 (in 7 ranges), indexes: (73 75 131 140 250 267 311)]";
"\U4e0d" = "<NSMutableIndexSet>[number of indexes: 7 (in 7 ranges), indexes: (162 178 212 214 237 254 259)]";
"\U4e11" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (7)]";
"\U4e16" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (132 301)]";
"\U4e4b" = "<NSMutableIndexSet>[number of indexes: 20 (in 20 ranges), indexes: (10 18 71 99 105 118 126 142 153 181 187 197 200 205 216 246 262 280 286 314)]";
"\U4e5d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (2)]";
"\U4e5f" = "<NSMutableIndexSet>[number of indexes: 4 (in 4 ranges), indexes: (24 86 123 312)]";
"\U4e8b" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (23 192 303)]";
"\U4e91" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (230)]";
"\U4ea6" = "<NSMutableIndexSet>[number of indexes: 4 (in 4 ranges), indexes: (77 233 283 317)]";
"\U4ead" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (20)]";
"\U4eba" = "<NSMutableIndexSet>[number of indexes: 4 (in 4 ranges), indexes: (125 229 243 295)]";
"\U4eca" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (282 285)]";
"\U4ee5" = "<NSMutableIndexSet>[number of indexes: 6 (in 6 ranges), indexes: (55 79 108 114 215 306)]";
"\U4ef0" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (95 130 204)]";
"\U4f5c" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (278)]";
"\U4fdb" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (203)]";
"\U4fe1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (120)]";
"\U4fee" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (21 42 220)]";
"\U4fef" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (101 129)]";
"\U5026" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (189)]";
"\U5167" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (143)]";
"\U5176" = "<NSMutableIndexSet>[number of indexes: 5 (in 5 ranges), indexes: (63 165 185 297 309)]";
"\U5217" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (61 292)]";
"\U521d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (11)]";
"\U5316" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (223)]";
"\U53c8" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (44)]";
"\U53ca" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (184)]";
"\U53d6" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (134)]";
"\U53e4" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (228)]";
"\U53ef" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (121)]";
"\U53f3" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (53)]";
"\U5408" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (249)]";
"\U540c" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (163)]";
"\U5411" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (199)]";
"\U548c" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (1 93)]";
"\U54b8" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (31)]";
"\U54c1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (103)]";
"\U54c9" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (239)]";
"\U55bb" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (261)]";
"\U55df" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (257)]";
"\U5617" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (253)]";
"\U56e0" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (145)]";
"\U56fa" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (265)]";
"\U5728" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (5)]";
"\U5730" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (34)]";
"\U5750" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (62)]";
"\U5916" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (154)]";
"\U5927" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (100 234)]";
"\U5929" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (87)]";
"\U592b" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (124 290)]";
"\U5951" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (251)]";
"\U5984" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (277)]";
"\U5a1b" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (119)]";
"\U5b87" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (97)]";
"\U5b99" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (98)]";
"\U5ba4" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (141)]";
"\U5bc4" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (146)]";
"\U5bdf" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (102)]";
"\U5c07" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (182 318)]";
"\U5c11" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (29)]";
"\U5c71" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (16 37)]";
"\U5cad" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (39)]";
"\U5cfb" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (38)]";
"\U5d07" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (36)]";
"\U5de6" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (52)]";
"\U5df1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (173)]";
"\U5df2" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (207)]";
"\U5e36" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (51)]";
"\U5e74" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (3)]";
"\U5e7d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (82)]";
"\U5f15" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (54)]";
"\U5f26" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (70)]";
"\U5f62" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (151)]";
"\U5f6d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (274)]";
"\U5f8c" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (279 313)]";
"\U5f97" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (171)]";
"\U600f" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (174)]";
"\U609f" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (138)]";
"\U60b2" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (289)]";
"\U60bc" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (258)]";
"\U60c5" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (83 190)]";
"\U60e0" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (91)]";
"\U611f" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (194 245 320)]";
"\U6168" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (195)]";
"\U61f7" = "<NSMutableIndexSet>[number of indexes: 5 (in 5 ranges), indexes: (112 136 218 264 308)]";
"\U6216" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (133 144)]";
"\U6240" = "<NSMutableIndexSet>[number of indexes: 7 (in 7 ranges), indexes: (107 147 168 186 201 298 305)]";
"\U62b1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (137)]";
"\U653e" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (149)]";
"\U6545" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (291)]";
"\U6558" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (81 293)]";
"\U6587" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (256 323)]";
"\U65af" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (322)]";
"\U65bc" = "<NSMutableIndexSet>[number of indexes: 6 (in 6 ranges), indexes: (13 167 172 226 263 321)]";
"\U65e2" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (188)]";
"\U65e5" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (85)]";
"\U6614" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (242 288)]";
"\U6620" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (50)]";
"\U6625" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (9)]";
"\U662f" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (84)]";
"\U6642" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (294)]";
"\U66a2" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (80 94)]";
"\U66ab" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (170)]";
"\U66ae" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (8)]";
"\U66f2" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (59)]";
"\U6703" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (12 14)]";
"\U6709" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (35 45 319)]";
"\U6717" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (88)]";
"\U671f" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (225)]";
"\U672a" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (252)]";
"\U6797" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (41)]";
"\U6975" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (115)]";
"\U6a02" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (122)]";
"\U6b21" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (64)]";
"\U6b23" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (166 202)]";
"\U6b64" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (33)]";
"\U6b72" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (4)]";
"\U6b7b" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (231 268)]";
"\U6b8a" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (159 302)]";
"\U6ba4" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (275)]";
"\U6bcf" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (240)]";
"\U6c23" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (89)]";
"\U6c34" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (60)]";
"\U6c38" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
"\U6cc1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (219)]";
"\U6d41" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (47 57)]";
"\U6d6a" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (150)]";
"\U6e05" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (46 90)]";
"\U6e4d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (49)]";
"\U6fc0" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (48)]";
"\U70ba" = "<NSMutableIndexSet>[number of indexes: 4 (in 4 ranges), indexes: (56 208 270 276)]";
"\U7121" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (66)]";
"\U7136" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (175)]";
"\U7336" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (211 284)]";
"\U751f" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (232 269)]";
"\U7531" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (247)]";
"\U7562" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (27)]";
"\U7570" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (304)]";
"\U7576" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (164)]";
"\U75db" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (238)]";
"\U7678" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (6)]";
"\U76db" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (72 106)]";
"\U76e1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (227)]";
"\U76ee" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (110)]";
"\U76f8" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (127)]";
"\U77e3" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (198 235)]";
"\U77e5" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (179 266)]";
"\U77ed" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (221)]";
"\U798a" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (22)]";
"\U7a3d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (15)]";
"\U7af9" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (43 68)]";
"\U7ba1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (69)]";
"\U7cfb" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (196)]";
"\U7d42" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (224)]";
"\U7d72" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (67)]";
"\U7fa4" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (25)]";
"\U8001" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (180)]";
"\U8005" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (316)]";
"\U807d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (117)]";
"\U80fd" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (213 260)]";
"\U81e8" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (255)]";
"\U81ea" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (176)]";
"\U81f3" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (28 183)]";
"\U81f4" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (310)]";
"\U8207" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (128)]";
"\U8208" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (217 244 307)]";
"\U820d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (157)]";
"\U82e5" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (248)]";
"\U8302" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (40)]";
"\U842c" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (158)]";
"\U862d" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (19)]";
"\U865b" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (271)]";
"\U8996" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (116 281 287)]";
"\U89bd" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (241 315)]";
"\U89c0" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (96)]";
"\U89f4" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (58 74)]";
"\U8a00" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (139)]";
"\U8a17" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (148)]";
"\U8a60" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (76)]";
"\U8a95" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (272)]";
"\U8af8" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (135)]";
"\U8c48" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (236)]";
"\U8ce2" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (26)]";
"\U8da3" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (156)]";
"\U8db3" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (78 113 177)]";
"\U8de1" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (210)]";
"\U8e81" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (161)]";
"\U8ff0" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (299)]";
"\U9047" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (169)]";
"\U904a" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (109)]";
"\U9077" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (193)]";
"\U9304" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (296)]";
"\U9577" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (30)]";
"\U9593" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (206)]";
"\U9670" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (17)]";
"\U9673" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (209)]";
"\U96a8" = "<NSMutableIndexSet>[number of indexes: 2 (in 2 ranges), indexes: (191 222)]";
"\U96c6" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (32)]";
"\U96d6" = "<NSMutableIndexSet>[number of indexes: 3 (in 3 ranges), indexes: (65 155 300)]";
"\U975c" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (160)]";
"\U985e" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (104)]";
"\U98a8" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (92)]";
"\U9a01" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (111)]";
"\U9ab8" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (152)]";
"\U9f4a" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (273)]";
}
NSString uses UTF-16, however, like UTF-8, UTF-16 is a variable-width encoding, if your character is not in the BMP (Basic Multilingual Plane, Plane 0), it will take 4 bytes (two code points) for each character instead of 2 bytes.
For example, NSString *inputString = @"大𩗴";, the string length is 3, and the converted structure will looks like this:
{
"\U5927" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
"\Ud865\Uddf4" = "<NSMutableIndexSet>[number of indexes: 1 (in 1 ranges), indexes: (1)]";
}
Instead of looking up code point manually by using -characterAtIndex:, yllan suggests [NSString rangeOfComposedCharacterSequenceAtIndex:]; could be a better solution if you want to iterate through each non-ASCII character.
I asked the other day on Twitter if I should expire beta app, and people are saying yes. So I made a quick solution for my beta:
The iOS app will show an UIAlertView two weeks after built, then
exit(0);itself after user dismissed the alert view.
First, I add following code snippet into my main view controller:
#ifdef BETA_EXPIRATION
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:@"MMM d yyyy"];
NSLocale *localeUS = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
[dateFormatter setLocale:localeUS];
NSDate *compileDate = [dateFormatter dateFromString:[NSString stringWithUTF8String:__DATE__]];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSWeekCalendarUnit
fromDate:compileDate
toDate:[NSDate date]
options:0];
// Expired after 2 weeks
if ([components week] > 1) {
UIAlertView *expirationAlert = [[UIAlertView alloc] initWithTitle:@"Beta Expired"
message:@"Thanks for testing beta for these two weeks"
delegate:self
cancelButtonTitle:@"Terminate Now"
otherButtonTitles:nil];
[expirationAlert show];
[expirationAlert release];
}
}
#pragma mark UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
exit(0);
}
#endif
The __DATE__ preprocessor constant represents the date that source code compiled, it will be replaced with a string literal in following form during compile time:
“mmm dd yyyy”
We can prepare a NSDateFormatter to translate __DATE__ string into NSDate, represent the time we compiled app.
Later, use NSDateComponents to compare compile date and current date with calendrical unit flag NSWeekCalendarUnit, so that we can know how many weeks had passed since the app was built. I use week for example here, but you can change to other units, like NSHourCalendarUnit, NSDayCalendarUnit, NSQuarterCalendarUnit etc.
Also, in this code snippet, the first seven days are calculated as “week zero,” following the next seven days for “week one.”
So, once users launch the app two weeks later, I will show them a UIAlertView, and terminate the app once they click the button.
I use #ifdef BETA_EXPIRATION ... #endif to limit the section of code to be built for AdHoc version only.
The easiest way to do that, is opening your Xcode project file, add value for “Preprocessor Macros” that in your target settings:

There’s no sample for this, the code snippet above is all you need.
Feel free you use it.
BTW, this can be easily hacked, so use with caution if you’re delivering some top secret apps for testing.
Last month, I wrote “How I add background image to UINavigationBar.” By using CALayer’s contents property, you can easily add a CGImage to your UINavigationBar.
And this time, I’m going to show you, how I add background image into UIViewController and to support both landscape and portrait mode.
For iPhone and iPod touch, you need a 480 by 480 pixels image (also a 960 by 960 pixels for retina display); and for iPad user, you need one 1024 by 1024 pixels image. So that you can use it as background image for both landscape and portrait mode:

Then you can set CGImage into UIViewController’s self.view.layer.contents, and use contentsRect to change the the sub-rectangle of contents that we should draw:
#import <QuartzCore/QuartzCore.h>
- (void)viewDidLoad {
self.view.layers.contents = (id)[UIImage imageNamed:@"myBackground"].CGImage;
if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
self.view.layer.contentsRect = CGRectMake(0.0, 0.0, 1.0, 320.0/480.0);
} else {
self.view.layer.contentsRect = CGRectMake(0.0, 0.0, 320.0/480.0, 1.0);
}
}
If want to support rotations, you will need to change contentRects again in - (void)didRotateFromInterfaceOrientation:.
P.S. If you want to prevent background image resized when UIToolbar shows up, you can change self.view.layer.contentsGravity from default kCAGravityResize to kCAGravityBottom.
@drunknbass wonder why not using +colorWithPatternImage: instead?
My concern here is memory, if I use +colorWithPatternImage: with a single large image, it will cost lots of memory than the way I used above. I’m not sure why this happened, but @drunknbass promised he will dig into this when he is lazy (enough).
For those who want to see how DDShareViewController can be customized, now you can try FrostyPlace 2.0.

It’s a $0.99 RSS reader for frostyplace.com.
And I extended DDShareViewController to support Facebook, Twitter and Plurk at the same time. You can see how the word counts and title description placed in Twitter and Plurk view controllers, while Facebook view controller still kept the original privacy button.
Once I cleaned up the source code, I will commit the changes back to DDShareViewController project over github.
So a little earlier Matt Drance opined about
-[NSNotificationCenter addObserver...usingBlock:], claiming it wasn’t so good. I personally love being able to specify a block to handle a notification, and especially love being able to have the observer run on the main thread, or on a queue of my…
I’m with Matt Drance. That’s exact the same reason I don’t use -[NSNotificationCenter addObserver...usingBlock:], and after reading Jim’s post, I become more certain my decision to avoid this API call is totally correct.
Also, it’s bad to keep removing and adding observer again and again in blocks when you want to receive more than one message for the same notification, that’s not “observing.”
If you’re observing notification for multiple times, just don’t use -[NSNotificationCenter addObserver...usingBlock:], it’s a BAD pattern.
I know Apple wants people to deal notifications in the way we use blocks as completion handler (I do love this idea, really), but blocks in here have the retain-loop side effect for most of the use cases, so…
This is a Problematic Designed API Call, Confirmed.
(Source: quatermain)
By design, the bottom right corner of Xcode 4 is reserved for Libraries pane, which contains different libraries, includes:
And each library has two different views: one is List View, provides detail description for each item, like title, summary, filename, etc.; the other is Icon View, which only shows the item icons.

And it looks fine on most of the libraries, except the Icon View of Code Snippet Library:

As you can see, Code Snippet Library’s Icon View only has two kinds of icons, one default snippet, and the other is user custom snippet. Unlike other libraries, you can hardly tell the difference between each item in here:
The information of the snippet icon is insufficient.
However, you can’t just put the source code on top of the icon, that will be damn too ugly.
To extend the existing concept (two code snippet icons, default snippet and user custom snippet), let’s check the snippet editor first:

If you look closer, the code snippet still has several metadata, shorter ones, that can be used on top of the icon, like:
If Apple placed these two on top of the icon, the icon view indeed looks better:

This proposal isn’t perfect, it has some worst user cases:
But still, I think this will improve the accessibility of current icon view design, and give us a better Code Snippet Library when using Xcode 4.
I had submitted this to radar://8689562.
Few weeks ago, I released MapKitDragAndDrop 3.1, a major change to switch away from synthesized by default. Since then, I got some emails about memory leaks, regressions, and suggestions.
By addressing those issues, I’m pleased to announce the MapKitDragAndDrop 3.2.
DDAnnotationView now comes with a new class method called +annotationViewWithAnnotation:reuseIdentifier:mapView:, which allows developers to create a draggable annotation view easily. It will return either DDAnnotationView (on iOS3) or drag-enabled MKPinAnnotationView (on iOS4):
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
static NSString * const kPinAnnotationIdentifier = @"PinIdentifier";
MKAnnotationView *draggablePinView = [self.mapView dequeueReusableAnnotationViewWithIdentifier:kPinAnnotationIdentifier];
if (draggablePinView) {
draggablePinView.annotation = annotation;
} else {
// Use class method to create DDAnnotationView (on iOS 3) or built-in draggble MKPinAnnotationView (on iOS 4).
draggablePinView = [DDAnnotationView annotationViewWithAnnotation:annotation reuseIdentifier:kPinAnnotationIdentifier mapView:self.mapView];
if ([draggablePinView isKindOfClass:[DDAnnotationView class]]) {
// draggablePinView is DDAnnotationView on iOS 3.
} else {
// draggablePinView instance will be built-in draggable MKPinAnnotationView when running on iOS 4.
}
}
return draggablePinView;
}
DDAnnotation now init the coordinate correctly.
Though DDAnnotation is running okay on iOS 4, I would suggest you use MKAnnotation or MKPlacemark on iOS 4 instead. The reason for this is because DDAnnotation is designed for iOS 3 when MKAnnotation only has readonly coordinate property (I re-decleare it to readwrite in header file), but starting from iOS 4, Apple had provided a method to change coordinate:
@protocol MKAnnotation <NSObject>
// Center latitude and longitude of the annotion view.
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@optional
// Title and subtitle for use by selection UI.
- (NSString *)title;
- (NSString *)subtitle;
// Called as a result of dragging an annotation view.
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_4_0);
@end
So you can change coordinate through that on iOS 4, and no longer need to re-declare the property.
Source code is licensed under MIT license.
You can download it from github.
Here’re some tips when I using Core Data on my iOS apps:
If you found your NSManagedObject can’t pass the validation, delete it from NSManagedObjectContext before saving. Otherwise, your app might crash in the next -save:.
When my NSManagedObjectContext -save: failed, I call -rollback. to reset.
When using NSManagedObjectContext -countForFetchRequest:error:, set NSFetchRequest setReturnObjectsAsFaults property to NO, then you can speed up the query (and get rid of faults).
If you only need specific attribute in your entity, fetch NSManagedObject with NSExpressionDescription:
NSExpressionDescription *expressionDescription = [[[NSExpressionDescription alloc] init] autorelease];
[expressionDescription setName:@"myAttribute"];
[expressionDescription setExpression:[NSExpression expressionForKeyPath:@"myAttribute"]];
[expressionDescription setExpressionResultType:NSStringAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
[request setResultType:NSDictionaryResultType];
Remember, NSFetchedResultsControllerDelegate is always on duty, even when you push your view controller (has NSFetchedResultsController) onto navigation stack. One way to stop NSFetchedResultsController from updating in background is to set NSFetchedResultsControllerDelegate to nil in -viewWillDisappear: and set them back in -viewWillAppear:.
If you had NSFetchedResultsController created, don’t change it’s fetchRequest property for re-fetching, it’s a read-only property. I know there’re hacks for this, but please, don’t do it.
albastru asked:
Hi
Thanks for publishing the drag and drop annotation component. I've noticed the assigned MKMapViewDelegate doesn't get its mapView:regionDidChangeAnimated: method called after doing one drag&drop.
I believe it's because you don't call the parent implementation for the touch events in the DDAnnotationView in all cases.
} else {
[super touchesEnded:touches withEvent:event];
}
This shouldn't be in the else. I think it should be executed at all times (it solved my problem).
Btw, was there an easier way of sending this message? Subscribing to tumblr was pretty painful.
Best,
Ovidiu Sabou
I had aware of this issue before, but calling super leads to other pin position issues on iOS 3, that’s why I kept this part unchanged. However, if the delegate method -mapView:regionDidChangeAnimated: is really important to you, then you should, like Ovidiu, always call super.
But seriously, if you really want to use MapKit’s dragging feature, just switch to iOS 4. As I said before, this is 100% good for you, and your customers too.
As for sending message, I just turned on MapKitDragAndDrop’s issues section over github, and added my email address in blog’s top left corner, so you can contact me with the way you feel like to.
loading…