2013年8月3日 星期六

假如七八十歲的人還在創新,我們問題就大了

原文 http://forum.inside.com.tw/viewthread.php?tid=1492

節錄:

一個多月以前我去台灣,在一個餐桌上,有一批年紀很大的企業家,頭髮都很白了,每個人都大談創新,怎麼創新?邊上有個人跟我講,台灣有希望,我想這麼大年紀的人還在創新。後來我說台灣沒希望了。 假如七八十歲的人還在創新,我們問題就大了,他們不相信年輕人比他們更會創新,其實他們應該是盡全力去努力幫助年輕人去創新,建個平台扶持他們創新、幫助他們創新。 所以我們認為比年輕人更聰明,那災難就出現了。

延伸閱讀

十九世紀的製冰業,派人在結冰的湖面或河裡,將冰塊鋸下給顧客使用,他們的工作後來被製冰廠取代,製冰廠又被電冰箱取代,假如他們願意擁抱改變,而不是去抗拒改變,他們將有機會轉進到新的技術領域,而不是眼睜睜看著自己的心血被別人取代。

老人用他們的想法創新,不接受小一輩的真正創新。

這想法就和冰塊製造廠老闆一直想創新冰塊,卻不接受電冰箱這種東西的情況類似。

對冰塊業創新是一條路,但是當電冰箱出來後,大家都能做冰塊,還守著舊思維思考怎麼運送冰塊好讓他不融化,而不去思考是否到處建小廠做冰塊,那註定失敗。

因為這是一種典範轉移、破壞(革命)性創新,而破壞性創新是不可能從原本的基礎上改進而來

同樣的問題出現在出版業、快遞業、ooxx行業,只思考怎麼更好的賣東西,而不是思考他們在賣的是東西還是資訊時,那註定會被淘汰

十九世紀的製冰業,派人在結冰的湖面或河裡,將冰塊鋸下給顧客使用,他們的工作後來被製冰廠取代,製冰廠又被電冰箱取代

有多少製冰業的老闆講創新時是在想製冰廠?製冰廠老闆有多少在思考電冰箱?

那些七八十歲的製冰廠老闆講創新時,我們都在弄電冰箱了,你相信他們和我們想的會視同一件事情嗎?

馬雲這次的觀點未必正確,不過他想講的東西是正確的,那些該放手的人就放手吧。

換個角度,台灣現在一線演員你 看得到多少3x, 4x的人?製作人呢?我們只看得到/聽得到楊佩佩之類6x,7x的人,3x/4x的人跑哪去了?是不存在還是整個被那些努力工作一輩子的人給壓住了?

當然很多東西需要時間養成,名氣、演技、成就都是,但是當這一代整個不在名單上時,這不叫做沒人成功,這叫做一種警訊

長輩活到90歲,工作到90,然後留給6x/7x的人擔子,6x/7x又活到100。這樣的企業/環境,真正新穎的想法怎麼出頭?

真正新穎的想法是沒有前例可循的, 提出來時,長輩一句你有經驗嗎?我有!沒有想法是能通過這句話的檢驗和長輩經驗門檻的

然後就是只能依循前例,大家繼續想怎麼更有效率的從河裡挖冰而不是去思考電冰箱怎麼用比較好(攤手

到了這地步,真正的創新就不可能了 XD

500 元的實價登錄網站

 

http://www.appledaily.com.tw/realtimenews/article/politics/20121105/150504/1

李鴻源還說,民眾自行製作「實價登錄地圖」,其實與內政部實價登錄網站不一樣,「我們是在蓋房子,他們在窗戶上貼了彩色玻璃」,但已交代,應讓網站使用更友善。

基本上,這是不可能做到的

只要還是政府公務員在主導整個案子的話

這不是說公務員不盡心、不盡力

或者是對案子抱著得過且過的心態

而是結構性使然

哪來的結構性?

一、(很多)政府專案的結案報告都是一堆..公務員在審

想想看,你們公司開了一個社群網站專案

然後外包給外面公司

定下案子的是覺得大家有我也要有的公司老闆

決定規格的是帶著老花眼鏡,有很多業務經驗,待了 10 年以上,只會用 Office 和 Yahoo 的業務主管

主導過程的是只想著準時下班,能閃則閃,網站成果和他沒一毛錢關係的櫃檯人員

外包公司問架構時,櫃檯人員都說可以

開發出草稿時,櫃檯人員也說可以

盡心盡力做出一個 UX 很好的網站時

主管就直接打槍:根據我多年經驗,這不是我們要的東西,重做!

再花 100% 精力做一版後

主管:根據我多年經驗,這個東西不合格

然後呢?專案人力、資源都快滿了

只好 60% 來一版

問櫃檯,櫃檯模擬兩可,okok

問主管,主管勉強點頭說可以

問老闆?

老闆就直接打槍:根據我多年經驗,這不是我們要的東西,重做!

再花 40% 精力做一版後

老闆:根據我多年經驗,這個東西不合格

然後時間到了

只好再花 20% 隨便改一改,趕快送出去

老闆出國考察

櫃檯幫忙遞交

主管不敢點頭答應,就開一個結案會議拉大家一起討論

大家看一看,那邊顏色不對、這邊圖片太小,這個版權宣告不正確

UX? UI? 那是啥鳥?

從頭到尾根本沒有人在乎這東西!

只有上面主管的思考是絕對的!

設計的再好、說服了再多人,都比不過最上面的摸不著頭緒、沒用過網站看一眼就說的一句話

這樣的公務員所「設計」出來的網站

能用才有鬼..

不過我沒接觸過政府資訊部門所開發的網站

都是一些小單位

所以以上說法也許有所偏頗(大概5-99%?)

還是請以實際經驗為準

--

不過公部門專案被主管打槍好像是常態就是了..

2013年8月1日 星期四

git alias 在 windows 7 上的位置

本來想弄個簡單的 git alias, 沒想到弄半天都過不了
一氣之下,就直接跑去改 .gitconfig 了

檔案位置在 HOME="$HOMEDRIVE$HOMEPATH"
例如 C:\Users\MyLogin\.gitconfig

參考來源
http://stackoverflow.com/questions/2114111/where-does-git-config-global-get-written-to


同場加映
使用 git 寫工作日誌

worklog = log --pretty=format:'%ad %s' --date=local --since=2.days

實際使用時,移動到git目錄,使用 git worklog > c:\0730.txt

2013年7月2日 星期二

sqlite 3 自定函数方法

关键字 sqlite3_create_function

这方法 ios 应该可以接受

来自

http://www.thismuchiknow.co.uk/?p=71

使用方法

sqlite3_create_function(sqliteDatabasePtr, "distance", 4, SQLITE_UTF8, NULL, &distanceFunc, NULL, NULL);

记得先定义函数

#define DEG2RAD(degrees) (degrees * 0.01745327) // degrees * pi over 180

static void distanceFunc(sqlite3_context *context, int argc, sqlite3_value **argv){// check that we have four arguments (lat1, lon1, lat2, lon2)assert(argc == 4);// check that all four arguments are non-nullif (sqlite3_value_type(argv[0]) == SQLITE_NULL || sqlite3_value_type(argv[1]) == SQLITE_NULL || sqlite3_value_type(argv[2]) == SQLITE_NULL || sqlite3_value_type(argv[3]) == SQLITE_NULL) {sqlite3_result_null(context);return;}// get the four argument valuesdouble lat1 = sqlite3_value_double(argv[0]);double lon1 = sqlite3_value_double(argv[1]);double lat2 = sqlite3_value_double(argv[2]);double lon2 = sqlite3_value_double(argv[3]);// convert lat1 and lat2 into radians now, to avoid doing it twice belowdouble lat1rad = DEG2RAD(lat1);double lat2rad = DEG2RAD(lat2);// apply the spherical law of cosines to our latitudes and longitudes, and set the result appropriately// 6378.1 is the approximate radius of the earth in kilometressqlite3_result_double(context, acos(sin(lat1rad) * sin(lat2rad) + cos(lat1rad) * cos(lat2rad) * cos(DEG2RAD(lon2) - DEG2RAD(lon1))) * 6378.1);}

sqlite 3 自定函数

关键字 sqlite3_create_function

这方法 ios 应该可以接受

来自

http://www.thismuchiknow.co.uk/?p=71

使用方法

sqlite3_create_function(sqliteDatabasePtr, "distance", 4, SQLITE_UTF8, NULL, &distanceFunc, NULL, NULL);

记得先定义函数

#define DEG2RAD(degrees) (degrees * 0.01745327) // degrees * pi over 180

static void distanceFunc(sqlite3_context *context, int argc, sqlite3_value **argv){// check that we have four arguments (lat1, lon1, lat2, lon2)assert(argc == 4);// check that all four arguments are non-nullif (sqlite3_value_type(argv[0]) == SQLITE_NULL || sqlite3_value_type(argv[1]) == SQLITE_NULL || sqlite3_value_type(argv[2]) == SQLITE_NULL || sqlite3_value_type(argv[3]) == SQLITE_NULL) {sqlite3_result_null(context);return;}// get the four argument valuesdouble lat1 = sqlite3_value_double(argv[0]);double lon1 = sqlite3_value_double(argv[1]);double lat2 = sqlite3_value_double(argv[2]);double lon2 = sqlite3_value_double(argv[3]);// convert lat1 and lat2 into radians now, to avoid doing it twice belowdouble lat1rad = DEG2RAD(lat1);double lat2rad = DEG2RAD(lat2);// apply the spherical law of cosines to our latitudes and longitudes, and set the result appropriately// 6378.1 is the approximate radius of the earth in kilometressqlite3_result_double(context, acos(sin(lat1rad) * sin(lat2rad) + cos(lat1rad) * cos(lat2rad) * cos(DEG2RAD(lon2) - DEG2RAD(lon1))) * 6378.1);}

2013年7月1日 星期一

iOS 撷图方法

来自 http://blog.csdn.net/monsterguaishou/article/details/8512180

截取本区域(self.view):

UIGraphicsBeginImageContext(CGSizeMake(self.view.frame.size.width,self.view.frame.size.height));

  

 [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];

  

 UIImage *viewImage UIGraphicsGetImageFromCurrentImageContext();

   UIGraphicsEndImageContext();   UIImageWriteToSavedPhotosAlbum(viewImagenilnilnil);

全屏截图:

UIWindow *screenWindow [[UIApplication sharedApplication] keyWindow];

   UIGraphicsBeginImageContext(screenWindow.frame.size);   [screenWindow.layer renderInContext:UIGraphicsGetCurrentContext()];   UIImage *viewImage UIGraphicsGetImageFromCurrentImageContext();

   UIGraphicsEndImageContext();   UIImageWriteToSavedPhotosAlbum(viewImagenilnilnil);

以上2种方法真机和模拟器都可以运行.在photo.app里可以看到照片

苹果最新开放的接口函数(全屏截图),已经有人试过了,不会reject:

CGImageRef UIGetScreenImage()

  

 CGImageRef img UIGetScreenImage();

   UIImagescImage=[UIImage imageWithCGImage:img];   UIImageWriteToSavedPhotosAlbum(scImagenilnilnil);

It still works,but only on-device (not in simulator) .

截图另存为指定名字:

UIWindow *screenWindow [[UIApplication sharedApplication] keyWindow];

 UIGraphicsBeginImageContext(screenWindow.frame.size);[screenWindow.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *screenshot UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext()NSData *screenshotPNG UIImagePNGRepresentation(screenshot);

 NSArray *paths NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMaskYES);NSString *documentsDirectory [paths objectAtIndex:0];

 NSError *error nil;[screenshotPNG writeToFile:[documentsDirectorystringByAppendingPathComponent:@"screenshot.png"] options:NSAtomicWriteerror:&error];

部分代码来自:http://stackoverflow.com/questions/692464/emailing-full-screen-of-iphone-app

没有ipad真机截图发布app的可以用此方法做个透明按钮点,哈哈.

 

//播放截图声音NSString* path = [[NSBundle mainBundle]pathForResource:@”photoShutter” ofType:@”caf”];//NSString *path = [[NSBundlebundleWithIdentifier:@"com.apple.UIKit"] pathForResource:@”Tink”ofType:@”aiff”];SystemSoundID soundID;AudioServicesCreateSystemSoundID((CFURLRef)[NSURLfileURLWithPath:path], &soundID);AudioServicesPlaySystemSound(soundID);// we don’t dispose of the sound to keep the sound in the cache forthe next timeAudioServicesDisposeSystemSoundID(soundID);

可以用这顺带播放一下声音

在 uiwebview 事件加入调整宽度的 viewport 设定

refer: http://stackoverflow.com/questions/15826714/how-to-size-uiwebview-in-a-uimodalpresentationformsheet

在 webview 事件加入 JS,塞入 viewport 设定

不过这是处理外部网页的方法

内部网页的话,还是请后台网页人员加入 viewport 设定比较好

- (void)webViewDidFinishLoad:(UIWebView *)webView

{

NSString* js =

@"var meta = document.createElement('meta'); "

@"meta.setAttribute( 'name', 'viewport' ); "

@"meta.setAttribute( 'content', 'width = 540px, initial-scale = 1.0, user-scalable = yes' ); "

@"document.getElementsByTagName('head')[0].appendChild(meta)";

[[self webView] stringByEvaluatingJavaScriptFromString: js];

}

iOS 登入 windows doamin

复习一下什么是 windows active domain

http://www.cnblogs.com/GreenGrass/archive/2012/08/16/2642303.html

所以,Windows Doamin/Active Domain 其实是 LDAP 的一个应用

因此,找 iOS 的 windows domain 登入方法时,可以从 ladp 开始找

例如 openldap

http://stackoverflow.com/questions/929120/search-open-directory-ldap-with-objective-c

2013年6月24日 星期一

iOS app 訊息功設計

這次的 app 需要一各訊息中心功能

不過一開始說的很簡陋,就依照最簡單的作法下去處理

結果後來因為訊息種類持續增加,前面就要加入訊息分類頁面

有了訊息分類就要顯示未讀訊息,包括在訊息分類頁面和乘載此頁面的tabbar controller

然後,因為加入了推播功能…收到推播時當然也應該要進行合理更新…

因為這些歷程,整各架構就大改四次

以下是這幾次架構的簡述

V1: 訊息展示頁面,使用 tableview controller

開啟時更新訊息,然後 insert to row

V2:訊息分類頁面,出現時更新訊息,取得未讀放到分類 icon 上

V3

由於更新訊息時是使用訊息分類頁面,反饋到 tabbar controller 更新 badge 的步驟太複雜(事件難控制)

所以改成使用 notification center 處理

然後又是一次大改..

V4

為了要支援推播,所以需要在接收到推播時更新訊息,然後用 notification center 事件更新

但是單純只改這部份的話,程式又會變得複雜

所以就把更新訊息功能單獨取出成立物件

並且修改成 程式進入前景、收到推播、進入訊息中心時更新訊息

更新後,使用notification center 通知更新頁面

這樣修改後

會有三個觸發程式更新訊息的時間點,進入前景、收到推播、進入訊息分類頁面時

事實上,前兩個應該就夠了

不過由於這個 app 是在企業內使用,有可能會在無法收到推播的環境下作業

因此加入了進入分頁時也會更新訊息的功能避免這種狀況

這些功能也從一開始存在於個別 VC 中的情況轉變成公用程式

但是因為公用程式太多太雜,最後又變成一個擁有自己參數和設定的獨立物件

在這過程中

會發現由於一開始的需求不確定,單純使用簡單的設計

但是後來隨著不斷的改進,慢慢的趨向了主流的設計模式

許多設計模式也許一開始覺得太..複雜用不上

那是很正常的

但是當功能開始複雜後,為了彼此間的整合,就會慢慢趨向許多設計模式的作法

因此,即使目前的功能簡單

但是短期內有可能大幅演進的時候

一開始多花點時間設計好架構

就可以大幅縮短日後的修改、維護所需花費的心力

而Best Practice 就是許多人在處理幾乎相同的工作時,歸納、精簡出來的實作範例

設計模式則是在處理「概念」類似的工作時,歸納出來的作法之抽象模型

Sharekit 心得 (轉)

用 sharekit 整合

twitter

http://blog.csdn.net/sjx19871225/article/details/8777951

facebook

http://blog.csdn.net/sjx19871225/article/details/8517120

2013年6月23日 星期日

FMDB 相關

http://akabeko.me/blog/2011/11/ios-sqlite-fmdb/#fmdb_04

大量插入時,可以使用 transaction 增進效能(差異很大)

[db beginTransaction]

for () {

}

[db commit];

[db close] 方法只有在 table scheme 有改變時才需要呼叫

而且每次重開都會影響效能(由原作回答,2013? /3/30)

http://stackoverflow.com/questions/15720272/when-to-close-sqlite-database-using-fmdb

[db intForQuery] 方法的效果是取得第一各欄位的資料回傳

所以要使用 select count(*) from table where xxxx 讓 fmdb 取得正確的資料

2013年6月21日 星期五

擺脫內建ui,快速自製-custom-uitabbar

來自 http://jwill.pixnet.net/blog/post/29978299-ios-%E6%93%BA%E8%84%AB%E5%85%A7%E5%BB%BAui%EF%BC%8C%E5%BF%AB%E9%80%9F%E8%87%AA%E8%A3%BD-custom-uitabbar

純備份

不知道這作法和使用其他 tabbarcontroller 套件比較如何

在 UIApplication 中加入:

[self.tabBarController.tabBar.superview.addSubview: fakeTabBG];

[self.tabBarController.tabBar.superview addSubview: fakeTab];

[self.tabBarController.tabBar.superview addSubview: fakeNavBar];

在自製的 fakeTabItem (TabBar 下面的 Tab,其實是以 UIButton 實作)

[fakeTabItem4 addTarget:self action:@selector(tabItemPressed:) forControlEvents:UIControlEventTouchUpInside];

每個 Tab Button trigger 原生 tabBarController

- (void)tabItemPressed: (UIButton *) sender

{

nowTabIndex = [[[NSNumber alloc] initWithInteger: sender.tag] intValue] -1;

self.tabBarController.selectedIndex = nowTabIndex;

[self chkPressed];

}

Thread safe Singleton 寫法

http://stackoverflow.com/questions/2199106/thread-safe-instantiation-of-a-singleton

+(MyClass *)sharedInstance

{

static MyClass *sharedInstance = nil;

static dispatch_once_t pred;

dispatch_once(&pred, ^{

sharedInstance = [MyClass alloc];

sharedInstance = [sharedInstance init];

});

return sharedInstance;

}

2013年6月20日 星期四

正規表示法 regular expression

大原則是分次取得包含內容的元素

最後才去掉標籤

當然情況比較複雜時就看需求了

以下會先使用 matchesInString 取得元素

然後用 replaceInString 將前後 tag 去掉留下中間的值

                

                NSString *aa = @"123123<span class=\"g\">span content</span>243242424";

                

                NSError *errRegex = NULL;

                

                NSRegularExpression *regex = [NSRegularExpression

                 regularExpressionWithPattern:@"<span class=\"g\">(.|\n)*?</span>"

                 options:NSRegularExpressionCaseInsensitive

                 error:&errRegex];

                

                NSUInteger countMatches = [regex numberOfMatchesInString:aa

                 options:0 range:NSMakeRange(0, [aa length])];

                [regex enumerateMatchesInString:aa options:0

                 range:NSMakeRange(0, [aa length])

                 usingBlock:^(NSTextCheckingResult *match,

                 NSMatchingFlags flags, BOOL *stop) {

                

                 NSLog(@"Ranges: %ld", [match numberOfRanges]);

                

                 NSString *matchFull = [aa substringWithRange:[match range]];

                 NSLog(@"Match: %@", matchFull);

                        

                        NSRegularExpression *regexx = [NSRegularExpression

                        regularExpressionWithPattern:@"<(.|\n)*?>"

                        options:NSRegularExpressionCaseInsensitive

                        error:&errRegex];

                        

                        NSString *modifiedString = [regexx

                                 stringByReplacingMatchesInString:matchFull

                                options:0 range:NSMakeRange(0, [matchFull length]) withTemplate:@""];

                        NSLog(@"%@", modifiedString);

                }];

                

                if (errRegex) {

                 NSLog(@"%@", errRegex);

                }

2013年6月19日 星期三

iOS 使用 webservice 方法

原文 http://bbs.csdn.net/topics/330170341

#import <UIKit/UIKit.h>

@interface Hello_SOAPViewController : UIViewController

{

        IBOutlet UITextField *nameInput;

        IBOutlet UILabel *greeting;

        NSMutableData *webData;

        NSMutableString *soapResults;

        NSXMLParser *xmlParser;

        BOOL *recordResults;

}

@property(nonatomic, retain) IBOutlet UITextField *nameInput;

@property(nonatomic, retain) IBOutlet UILabel *greeting;

@property(nonatomic, retain) NSMutableData *webData;

@property(nonatomic, retain) NSMutableString *soapResults;

@property(nonatomic, retain) NSXMLParser *xmlParser;

-(IBAction)buttonClick: (id) sender;

@end

#import "Hello_SOAPViewController.h"

@implementation Hello_SOAPViewController

@synthesize greeting, nameInput, webData, soapResults, xmlParser;

-(IBAction)buttonClick:(id)sender

{

        recordResults = FALSE;

        

        NSString *soapMessage = [NSString stringWithFormat:

        @"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"

        "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"

        "<soap:Body>\n"

"<Hello xmlns=\"http://viium.com/\">\n"

        "<name>%@</name>\n"

"</Hello>\n"

        "</soap:Body>\n"

        "</soap:Envelope>\n", nameInput.text

        ];

        NSLog(soapMessage);

        

        NSURL *url = [NSURL URLWithString:@"http://viium.com/WebService/HelloWorld.asmx"];

        NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];

        NSString *msgLength = [NSString stringWithFormat:@"%d", [soapMessage length]];

        

        [theRequest addValue: @"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];

        [theRequest addValue: @"http://viium.com/Hello" forHTTPHeaderField:@"SOAPAction"];

        [theRequest addValue: msgLength forHTTPHeaderField:@"Content-Length"];

        [theRequest setHTTPMethod:@"POST"];

        [theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];

        

        NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

        

        if( theConnection )

        {

                webData = [[NSMutableData data] retain];

        }

        else

        {

                NSLog(@"theConnection is NULL");

        }

        

        [nameInput resignFirstResponder];

        

}

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

{

        [webData setLength: 0];

}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

{

        [webData appendData:data];

}

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

{

        NSLog(@"ERROR with theConenction");

        [connection release];

        [webData release];

}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection

{

        NSLog(@"DONE. Received Bytes: %d", [webData length]);

        NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

        NSLog(theXML);

        [theXML release];

        

        if( xmlParser )

        {

                [xmlParser release];

        }

        

        xmlParser = [[NSXMLParser alloc] initWithData: webData];

        [xmlParser setDelegate: self];

        [xmlParser setShouldResolveExternalEntities: YES];

        [xmlParser parse];

        

        [connection release];

        [webData release];

}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName

attributes: (NSDictionary *)attributeDict

{

        if( [elementName isEqualToString:@"HelloResult"])

        {

                if(!soapResults)

                {

                        soapResults = [[NSMutableString alloc] init];

                }

                recordResults = TRUE;

        }

}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string

{

        if( recordResults )

        {

                [soapResults appendString: string];

        }

}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName

{

        if( [elementName isEqualToString:@"HelloResult"])

        {

                recordResults = FALSE;

                greeting.text = soapResults;

                [soapResults release];

                soapResults = nil;

        }

}

/*

Implement loadView if you want to create a view hierarchy programmatically

- (void)loadView {

}

*/

/*

Implement viewDidLoad if you need to do additional setup after loading the view.

- (void)viewDidLoad {

        [super viewDidLoad];

}

*/

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

        // Return YES for supported orientations

        return (interfaceOrientation == UIInterfaceOrientationPortrait);

}

- (void)didReceiveMemoryWarning {

        [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview

        // Release anything that's not essential, such as cached data

}

- (void)dealloc

{

        [xmlParser release];

        [super dealloc];

}

@end

關於 UUID

個人解法

+(NSString*)getUUID {

NSUserDefaults* pref = [NSUserDefaults standardUserDefaults];

NSString* uuid = [pref stringForKey:LOG_KEY_UUID];

if (uuid == nil || [uuid length]<=0) {

CFUUIDRef uuidObject = CFUUIDCreate(kCFAllocatorDefault);

uuid = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidObject);

[pref setValue:uuid forKey:LOG_KEY_UUID] ;

[pref synchronize];

CFRelease(uuidObject);

}

return uuid;

}

官方解法

http://www.neatstudio.com/show-2040-1.shtml

  1. #define NEW_UUID_KEY    @"uuid_created_by_developer"  
  2.    
  3. @interface NewUUID : NSObject {  
  4.    
  5.     NSString *uuid;  
  6.    
  7. }  
  8.    
  9. + (NSString *)identifier;  
  10.    
  11. @property (nonatomic, retain) NSString *uuid;  
  12.    
  13. @end  
  1. #import <Foundation/Foundation.h>  
  2.    
  3. #import "NewUUID.h"  
  4.    
  5. @implementation NewUUID  
  6.    
  7. @synthesize uuid;  
  8.    
  9. - (id)init {  
  10.     self = [super init];  
  11.     if (self) {  
  12.         uuid = NULL;  
  13.         return self;  
  14.     }  
  15.    
  16.     return nil;  
  17. }  
  18.    
  19. + (id)_instance {  
  20.     static id obj = nil;  
  21.     if( nil == obj ) {  
  22.         obj = [[self alloc] init];  
  23.     }  
  24.    
  25.     return obj;  
  26. }  
  27.    
  28. + (NSString *)identifier {  
  29.    
  30.     NSUserDefaults *handler = [NSUserDefaults standardUserDefaults];  
  31.     [[NewUUID _instance] setUuid:[NSString stringWithFormat:@"%@", [handler objectForKey:NEW_UUID_KEY]]];  
  32.    
  33.     if (NULL == [[NewUUID _instance] uuid] || 46 > [[[NewUUID _instance] uuid] length]) {  
  34.    
  35.         CFUUIDRef uuid = CFUUIDCreate(NULL);  
  36.         CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid);  
  37.    
  38.         NSString *result = [NSString stringWithFormat:@"%@-%@", @"new-uuid-", uuidStr];  
  39.    
  40.         CFRelease(uuidStr);  
  41.         CFRelease(uuid);  
  42.    
  43.         [[NewUUID _instance] setUuid:result];  
  44.    
  45.    
  46.         [handler setObject:[[NewUUID _instance] uuid] forKey:NEW_UUID_KEY];  
  47.         [handler synchronize];  
  48.     }  
  49.    
  50.     return [[NewUUID _instance] uuid];  
  51. }  
  52.    
  53. @end  

2013年6月10日 星期一

HTML img 標籤預設圖片用法

來源

http://stackoverflow.com/questions/980855/inputting-a-default-image-in-case-the-src-arribute-of-an-html-img-is-not-valid

html 中,image src 不存在時,使用替代圖片的作法

<img src="foo.jpg" onerror="this.src='error.jpg';">

不過使用 onerror 處理圖片有一個問題,當 error 圖片不存在時,會進入無窮迴圈(onerror > onerror > onerror… )

另一個方法是用

object 中的圖片存在時,就不會呼叫 img 內容

在 ie8 中,需要搭配 DTD for HTML 4.01 使用(不知道使用 HTML 5 標籤是否有用..)

2013年5月20日 星期一

iOS 5,iOS 6 自動旋轉設定

iOS 5 和 ios6 兼容的自動旋轉設定有點坑人..

首先,專案設定有可支援方向

這設定是全域性的,整個 app 能支援的方向都在這邊設定

但是個別的 VC 旋轉設定要在各 VC 中自行設定

而 iOS 5, iOS 6 的設定方法並不相同

iOS 5

- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

//檢查是否為可旋轉方向

}

// 設定旋轉後配置,可用其他方法

- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {

// in iOS statusBarOrientation is correct than all other orientation changes

if(UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))

self.view.frame = CGRectMake(0,0,1024,768);

else

self.view.frame = CGRectMake(0,0,768,1024);

}

iOS 6

如果每個 VC 都要旋轉,就算了

如果不是,在最底層的 VC 要先設定如下方法,禁用自動旋轉

- (BOOL)shouldAutorotate {

return NO;

}

然後在需要旋轉的 VC 使用

- (BOOL)shouldAutorotate{

return YES;

}

- (NSUInteger)supportedInterfaceOrientations{

return UIInterfaceOrientationMaskAllButUpsideDown;

}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{

return UIInterfaceOrientationLandscapeRight;

}

此外,在 ios 6 底下

window 需要设置 rootViewController,[window addSubview:navController.view]; 无效;

shouldAutorotate 在最底层设置才有效;

presentModalViewController 下用之前的自动旋转控制无效,须用 category 解决。

參考資料

http://fann.im/blog/2012/10/22/autorotation-changes-in-ios-6/

2013年5月15日 星期三

jqtouch animation failed on iPhone 5

恭喜那些衰到要修改 2009/11 jqtouch 版本在 iphone5 上動畫的人

Gratz to those doomed to correct jqtouch (version around 2009/11 around) and lucky reach here

jqtouch (2009/11) 版本在 iphone 5 上面有一些奇怪的問題

例如 slide 動畫在 iPhone 4 上面可以正常運作

但是在 iphone 5 上面不行

jQTouch has some weird animation behavior on iPhone 5

kind of slide animation works fine on iPhone 4 but not iPhone 5

處理的方法很簡單也很詭異

對不正常運作的方法綁定一個無意義動作即可

例如

if’s wasy to correct in a weird way..

bind these link button an animation event with meaningless action,

sort like:

$(‘#somediv’).bind(‘pageAnimationStart’, function(){

        $(‘somediv’).text($(‘somediv’).text());

});

$(‘#somediv’).bind(‘pageAnimationEnd’, function(){

        $(‘somediv’).text($(‘somediv’).text());

});

這看起來像是jQTouch 對於有事件的 div 執行動畫時的方法不同

不過因為這版本實在太舊了,懶得深入去追查

因此就模仿其他綁定動畫事件後正常的div 進行類似的動作

I guess this comes from jQTouch (2009/11 version) handles animation in different way with those bind page animation compared to those without binding.

Since this version is just too old, I don’t wanna spend lots time to track and find the bug, I decide to copy the actions from behave fine to those behave badly, and here is the result..

Quite easy code, spend 2 days tho.. because I tried lots normal solution on them but failed orz

2013年5月13日 星期一

cocoa 字元編碼

這幾天在處理一個登入介面

不過傳送密碼時碰到問題了

由於密碼中有特殊字元,諸如 @+等字元

而這些字元在 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding 中並不會被編碼

所以 server 端收到後,會有處理上的問題

因此必須強制編碼

http://stackoverflow.com/questions/3423545/objective-c-iphone-percent-encode-a-string

上面的連結提供了一個不錯的方式處理

其他相關的知識

cocoa coding

http://secondflush2.blog.fc2.com/blog-entry-62.html

http://stackoverflow.com/questions/2467844/convert-utf-8-encoded-nsdata-to-nsstring

字串非 null-terminated

NSString* newStr = [[[NSString alloc] initWithData:theData

encoding:NSUTF8StringEncoding] autorelease];

null terminated 時,使用 stringWithUTF8String 避免結尾的 \0 產生多餘字元

If the data is null-terminated, you should instead use -stringWithUTF8String: to avoid the extra \0 at the end.

NSString* newStr = [NSString stringWithUTF8String:[theData bytes]];

NSString to ASCII 轉成 ASCII

NSString *string = @"2";

int asciiCode = [string characterAtIndex:0]; //50

base64-utf8编码的NSString转化为NSString

http://blog.csdn.net/chenghxc/article/details/8618580

1、NSData+Base64.h

[plain] view plaincopyprint?

  1. #import <Foundation/Foundation.h>  
  2.   
  3. void *NewBase64Decode(  
  4.     const char *inputBuffer,  
  5.     size_t length,  
  6.     size_t *outputLength);  
  7.   
  8. char *NewBase64Encode(  
  9.     const void *inputBuffer,  
  10.     size_t length,  
  11.     bool separateLines,  
  12.     size_t *outputLength);  
  13.   
  14. @interface NSData (Base64)  
  15.   
  16. + (NSData *)dataFromBase64String:(NSString *)aString;  
  17. - (NSString *)base64EncodedString;  
  18.   
  19. @end  

2、NSData+Base64.m

[plain] view plaincopyprint?

  1. #import "NSData+Base64.h"  
  2.   
  3. //  
  4. // Mapping from 6 bit pattern to ASCII character.  
  5. //  
  6. static unsigned char base64EncodeLookup[65] =  
  7.     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  
  8.   
  9. //  
  10. // Definition for "masked-out" areas of the base64DecodeLookup mapping  
  11. //  
  12. #define xx 65  
  13.   
  14. //  
  15. // Mapping from ASCII character to 6 bit pattern.  
  16. //  
  17. static unsigned char base64DecodeLookup[256] =  
  18. {  
  19.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  20.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  21.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63,   
  22.     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx,   
  23.     xx,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,   
  24.     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx,   
  25.     xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,   
  26.     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx,   
  27.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  28.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  29.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  30.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  31.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  32.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  33.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  34.     xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,   
  35. };  
  36.   
  37. //  
  38. // Fundamental sizes of the binary and base64 encode/decode units in bytes  
  39. //  
  40. #define BINARY_UNIT_SIZE 3  
  41. #define BASE64_UNIT_SIZE 4  
  42.   
  43. //  
  44. // NewBase64Decode  
  45. //  
  46. // Decodes the base64 ASCII string in the inputBuffer to a newly malloced  
  47. // output buffer.  
  48. //  
  49. //  inputBuffer - the source ASCII string for the decode  
  50. //  length - the length of the string or -1 (to specify strlen should be used)  
  51. //  outputLength - if not-NULL, on output will contain the decoded length  
  52. //  
  53. // returns the decoded buffer. Must be free'd by caller. Length is given by  
  54. //  outputLength.  
  55. //  
  56. void *NewBase64Decode(  
  57.     const char *inputBuffer,  
  58.     size_t length,  
  59.     size_t *outputLength)  
  60. {  
  61.     if (length == -1)  
  62.     {  
  63.         length = strlen(inputBuffer);  
  64.     }  
  65.       
  66.     size_t outputBufferSize =  
  67.         ((length+BASE64_UNIT_SIZE-1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE;  
  68.     unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize);  
  69.       
  70.     size_t i = 0;  
  71.     size_t j = 0;  
  72.     while (i < length)  
  73.     {  
  74.         //  
  75.         // Accumulate 4 valid characters (ignore everything else)  
  76.         //  
  77.         unsigned char accumulated[BASE64_UNIT_SIZE];  
  78.         size_t accumulateIndex = 0;  
  79.         while (i < length)  
  80.         {  
  81.             unsigned char decode = base64DecodeLookup[inputBuffer[i++]];  
  82.             if (decode != xx)  
  83.             {  
  84.                 accumulated[accumulateIndex] = decode;  
  85.                 accumulateIndex++;  
  86.                   
  87.                 if (accumulateIndex == BASE64_UNIT_SIZE)  
  88.                 {  
  89.                     break;  
  90.                 }  
  91.             }  
  92.         }  
  93.           
  94.         //  
  95.         // Store the 6 bits from each of the 4 characters as 3 bytes  
  96.         //  
  97.         // (Uses improved bounds checking suggested by Alexandre Colucci)  
  98.         //  
  99.         if(accumulateIndex >= 2)    
  100.             outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4);    
  101.         if(accumulateIndex >= 3)    
  102.             outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2);    
  103.         if(accumulateIndex >= 4)    
  104.             outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3];  
  105.         j += accumulateIndex - 1;  
  106.     }  
  107.       
  108.     if (outputLength)  
  109.     {  
  110.         *outputLength = j;  
  111.     }  
  112.     return outputBuffer;  
  113. }  
  114.   
  115. //  
  116. // NewBase64Encode  
  117. //  
  118. // Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced  
  119. // output buffer.  
  120. //  
  121. //  inputBuffer - the source data for the encode  
  122. //  length - the length of the input in bytes  
  123. //  separateLines - if zero, no CR/LF characters will be added. Otherwise  
  124. //      a CR/LF pair will be added every 64 encoded chars.  
  125. //  outputLength - if not-NULL, on output will contain the encoded length  
  126. //      (not including terminating 0 char)  
  127. //  
  128. // returns the encoded buffer. Must be free'd by caller. Length is given by  
  129. //  outputLength.  
  130. //  
  131. char *NewBase64Encode(  
  132.     const void *buffer,  
  133.     size_t length,  
  134.     bool separateLines,  
  135.     size_t *outputLength)  
  136. {  
  137.     const unsigned char *inputBuffer = (const unsigned char *)buffer;  
  138.       
  139.     #define MAX_NUM_PADDING_CHARS 2  
  140.     #define OUTPUT_LINE_LENGTH 64  
  141.     #define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE)  
  142.     #define CR_LF_SIZE 2  
  143.       
  144.     //  
  145.     // Byte accurate calculation of final buffer size  
  146.     //  
  147.     size_t outputBufferSize =  
  148.             ((length / BINARY_UNIT_SIZE)  
  149.                 + ((length % BINARY_UNIT_SIZE) ? 1 : 0))  
  150.                     * BASE64_UNIT_SIZE;  
  151.     if (separateLines)  
  152.     {  
  153.         outputBufferSize +=  
  154.             (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE;  
  155.     }  
  156.       
  157.     //  
  158.     // Include space for a terminating zero  
  159.     //  
  160.     outputBufferSize += 1;  
  161.   
  162.     //  
  163.     // Allocate the output buffer  
  164.     //  
  165.     char *outputBuffer = (char *)malloc(outputBufferSize);  
  166.     if (!outputBuffer)  
  167.     {  
  168.         return NULL;  
  169.     }  
  170.   
  171.     size_t i = 0;  
  172.     size_t j = 0;  
  173.     const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length;  
  174.     size_t lineEnd = lineLength;  
  175.       
  176.     while (true)  
  177.     {  
  178.         if (lineEnd > length)  
  179.         {  
  180.             lineEnd = length;  
  181.         }  
  182.   
  183.         for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE)  
  184.         {  
  185.             //  
  186.             // Inner loop: turn 48 bytes into 64 base64 characters  
  187.             //  
  188.             outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];  
  189.             outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)  
  190.                 | ((inputBuffer[i + 1] & 0xF0) >> 4)];  
  191.             outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2)  
  192.                 | ((inputBuffer[i + 2] & 0xC0) >> 6)];  
  193.             outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F];  
  194.         }  
  195.           
  196.         if (lineEnd == length)  
  197.         {  
  198.             break;  
  199.         }  
  200.           
  201.         //  
  202.         // Add the newline  
  203.         //  
  204.         outputBuffer[j++] = '\r';  
  205.         outputBuffer[j++] = '\n';  
  206.         lineEnd += lineLength;  
  207.     }  
  208.       
  209.     if (i + 1 < length)  
  210.     {  
  211.         //  
  212.         // Handle the single '=' case  
  213.         //  
  214.         outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];  
  215.         outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)  
  216.             | ((inputBuffer[i + 1] & 0xF0) >> 4)];  
  217.         outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2];  
  218.         outputBuffer[j++] = '=';  
  219.     }  
  220.     else if (i < length)  
  221.     {  
  222.         //  
  223.         // Handle the double '=' case  
  224.         //  
  225.         outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];  
  226.         outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4];  
  227.         outputBuffer[j++] = '=';  
  228.         outputBuffer[j++] = '=';  
  229.     }  
  230.     outputBuffer[j] = 0;  
  231.       
  232.     //  
  233.     // Set the output length and return the buffer  
  234.     //  
  235.     if (outputLength)  
  236.     {  
  237.         *outputLength = j;  
  238.     }  
  239.     return outputBuffer;  
  240. }  
  241.   
  242. @implementation NSData (Base64)  
  243.   
  244. //  
  245. // dataFromBase64String:  
  246. //  
  247. // Creates an NSData object containing the base64 decoded representation of  
  248. // the base64 string 'aString'  
  249. //  
  250. // Parameters:  
  251. //    aString - the base64 string to decode  
  252. //  
  253. // returns the autoreleased NSData representation of the base64 string  
  254. //  
  255. + (NSData *)dataFromBase64String:(NSString *)aString  
  256. {  
  257.     NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];  
  258.     size_t outputLength;  
  259.     void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);  
  260.     NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];  
  261.     free(outputBuffer);  
  262.     return result;  
  263. }  
  264.   
  265. //  
  266. // base64EncodedString  
  267. //  
  268. // Creates an NSString object that contains the base 64 encoding of the  
  269. // receiver's data. Lines are broken at 64 characters long.  
  270. //  
  271. // returns an autoreleased NSString being the base 64 representation of the  
  272. //  receiver.  
  273. //  
  274. - (NSString *)base64EncodedString  
  275. {  
  276.     size_t outputLength;  
  277.     char *outputBuffer =  
  278.         NewBase64Encode([self bytes], [self length], true, &outputLength);  
  279.       
  280.     NSString *result =  
  281.         [[[NSString alloc]  
  282.             initWithBytes:outputBuffer  
  283.             length:outputLength  
  284.             encoding:NSASCIIStringEncoding]  
  285.         autorelease];  
  286.     free(outputBuffer);  
  287.     return result;  
  288. }  
  289.   
  290. @end  

3、NSString+Base64.h

[plain] view plaincopyprint?

  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface NSString (Base64)  
  4. + (NSString *)base64Encode:(NSString *)plainText;  
  5. + (NSString *)base64Decode:(NSString *)base64String;  
  6. @end  

4、NSString+Base64.m

[plain] view plaincopyprint?

  1. #import "NSString+Base64.h"  
  2. #import "NSData+Base64.h"  
  3. @implementation NSString (Base64)  
  4.   
  5. + (NSString *)base64Encode:(NSString *)plainText  
  6. {  
  7.     NSData *plainTextData = [plainText dataUsingEncoding:NSUTF8StringEncoding];  
  8.     NSString *base64String = [plainTextData base64EncodedString];  
  9.     return base64String;  
  10. }  
  11.   
  12. + (NSString *)base64Decode:(NSString *)base64String  
  13. {  
  14.     NSData *plainTextData = [NSData dataFromBase64String:base64String];  
  15.     NSString *plainText = [[NSString alloc] initWithData:plainTextData encoding:NSUTF8StringEncoding];  
  16.     return plainText;  
  17. }  
  18. @end  

5、实例代码:

[plain] view plaincopyprint?

  1. #import "AppDelegate.h"  
  2. #import "NSString+Base64.h"  
  3.   
  4. @implementation AppDelegate  
  5.   
  6. @synthesize window = _window;  
  7.   
  8. - (void)dealloc  
  9. {  
  10.     [super dealloc];  
  11. }  
  12.       
  13. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification  
  14. {  
  15.     NSString *str = @"未加密字符串";  
  16.     NSLog(@"nsstring: %@",str);  
  17.     NSString *base64utf8 = [NSString base64Encode:str];  
  18.     NSLog(@"base64utf8: %@",base64utf8);  
  19.     NSLog(@"nsstring: %@",[NSString base64Decode:base64utf8]);  
  20. }  
  21.   
  22. @end  

2013年5月9日 星期四

MAC 使用 intellij 開發 Grails 環境配置

  1. 安裝 homebrew

        使用 brew install grails 安裝最新版 grails

        需要其他版本的話

http://blog.jdriven.com/2012/09/quick-tip-installing-a-specific-grails-version-on-os-x-using-homebrew/

        使用 brew version grails 取得版本

        使用以下指令和版本的 hash coe 安裝不同版本

cd `brew --prefix`                              

git checkout 624e065 Library/Formula/grails.rb   # use output of "brew versions"

brew install grails                              # install grails

git checkout -- Library/Formula/grails.rb        # reset formula

        然後使用 brew switch grails version 切換版本

        如果新建/匯入專案時,IDEA 要求提供 Grails SDK 的話

        /usr/local/Cellar/grails/<version>/libexec

        純手工搭建方法

        http://thewidgetsmith.com/2013/01/grails-development-environment-set-up-os-x/

2013年5月8日 星期三

Mobile analytics 小測試

GAI - Goole Analytics

有提供即時的資訊

不過即時資訊提供的資訊不多

統計資訊大概要等各4小時才可用

功能..算是最複雜的吧

不過也可能是 UI 的不友善導致..

Flurry

整合的不錯,跟 GAI 有得拼

資訊統計時間有點久

初次使用時,三個小時後依然沒有看到資訊

明明手冊上面說只要一個小時..

慢到我都回頭去看看是否有缺少什麼設定..

看完之後還是沒資料 orz

只好隔天再來補完這段

修正:其實速度不錯,我昨天看錯圖表了

Localytics

iOS API 的方便性不是很好

需要自己在 Application Events 中加入 resume/stop/upload 方法

資料顯示的蠻快的

畫面風格很長簡潔,收費項目也很俐落的穿插在各個功能之中..

如果只是想看比較簡單的使用行為,算是蠻方便的

至於比較複雜的行為分析與追蹤,要等之後資料量變大後,再來開啟 premium 功能測試

不過 Localytics 竟然連 screen 都算在 premium 功能中…這真是令人噁心

MixPanel

SDK 便利性算是最差了(雖然也沒差到哪邊)

和 Localytics 算是互有勝場,不過都比不上 FLurry 方便

資料非常即時

不過感覺上用來追蹤(少數)使用者行為比較方便

但是會有這感覺和按鈕字體很大,不太好挑資料有點關係 :p

此外,介紹影片很多也很用心

目前 2013年5月9日 的 Mixpanel 已經跟 2010 年介紹的差異很大了

http://www.inside.com.tw/2010/11/09/mixpanel-realtime-analytics-for-web-and-mobile-applications

Inhouse app installs failed after provisioning updated

in-house app 在 provisioning profile 更新後,馬上就陷入無法安裝情況

以下是 console log

2013年5月7日 星期二

iOS ipa 結構

全新製作的流程

http://itools.hk/article59.htm

對 ipa 有興趣的話,可以看這邊的資源

http://blog.csdn.net/chengyakun11/article/details/8497733

iOS 使用的安裝格式

http://www.helmsmansoft.com/index.php/archives/1824

ipa 結構

http://floatlearning.com/2011/11/re-signing-an-ios-app-without-xcode/

文章介紹了 ipa 的檔案結構

iTunesArtwork

iTunesMetadata

Payload - App Binary //檔案

         - Bundle resources //資源

         - embedded.mobileprovision // provisioning 檔案

         - _CodeSignature // code sign

一般來說,dev app 並不需要 entitlement 檔案

除非有需要使用到需由 entitlement 指定的 push/iCloud 或類似服務

re-signing app

如果想要更新 app 的 provisioning

可以使用 /use/bun/codesign 處理

這樣的機會也許不會太多

不過當app 只有 mobile provisioning 改變的情況下

也許會希望使用這種作法

(新增裝置時,加入裝置的 UDID 後,需要更新 provision 檔案並重新簽署

重新簽署動作的 script

https://gist.github.com/mediabounds/1367348

AppResigner

http://www.gorbster.net/blog/archives/273

重新簽署 app 的 app (只認 .app 檔案,不認 .ipa)

Apple Developer Program 新版頁面 2013年5月8日

前陣子剛寫過一篇

沒想到這麼快就改版了

沒想到這麼快就看不懂了..

沒想到這麼快就要重寫一次了 orz

ADP overview 頁面

PastedGraphic-2013-05-8-10-40.png

Certificates 頁面

這次 Apple 把 APNS certificates 分出來了

PastedGraphic1-2013-05-8-10-40.png

一個帳號應該只會有一個的 dev certificates

PastedGraphic2-2013-05-8-10-40.png

Production certificates 頁面

APNS

PastedGraphic3-2013-05-8-10-40.png

同樣,應該只會有一個的 production certificates

PastedGraphic4-2013-05-8-10-40.png

App ID 頁面

PastedGraphic5-2013-05-8-10-40.png

Devices 頁面

PastedGraphic6-2013-05-8-10-40.png

Dev Provisioning 頁面

PastedGraphic8-2013-05-8-10-40.png

Prod Provisioning 頁面

PastedGraphic10-2013-05-8-10-40.png

整體來說

這次改版後,資料的預覽程度有增加,不用點進去就可以看到詳細資訊

不過流程有點混亂..

最後G一下

Apple 不知道在搞什麼鬼

前兩天(5/6)建立新的 app id 和相關設定(push)後

竟然出現了之前刪除的 certificates,導致我擁有三個同名的 dev vert,兩個 prod certificates

然後 Xcode 就風中殘燭不知如何編譯..

刪除重複檔案後,雖然把所有的 prov 都使用新的 cert 簽署

而理論上之前已經簽署的檔案也不該有問題

但是之前用 PhoneGap 封裝的 ipa 還是出現無法安裝問題…

取得出現頻率最高資料

mark 一下,晚點看

來源 http://d.hatena.ne.jp/tanaponchikidun/searchdiary?word=%2A%5Bobjective-c%5D

// NSNumber配列を初期化

NSArray *numbers = @[@9,@4,@5,@1,@1,@17,@9,@6,@9];

// 昇順ソート

NSArray *asecNumbers = [numbers sortedArrayUsingSelector:@selector(compare:)];

NSLog(@"asecNumbers = %@",asecNumbers);

// 降順ソート

NSSortDescriptor *descDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO];

NSArray *descNumbers = [numbers sortedArrayUsingDescriptors:@[descDescriptor]];

NSLog(@"descNumbers = %@",descNumbers);

// 最大、最小、平均、合計値の取得

NSLog(@"max of numbers = %@",[numbers valueForKeyPath:@"@max.self"]);

NSLog(@"min of numbers = %@",[numbers valueForKeyPath:@"@min.self"]);

NSLog(@"avg of numbers = %@",[numbers valueForKeyPath:@"@avg.self"]);

NSLog(@"sum of numbers = %@",[numbers valueForKeyPath:@"@sum.self"]);

// 重複データを取り除く

NSArray *distinctNumbers = [numbers valueForKeyPath:@"@distinctUnionOfObjects.self"];

NSLog(@"distinctNumbers = %@",distinctNumbers);

// 6以上のデータを取得

NSPredicate *over6Pred = [NSPredicate predicateWithFormat:@"SELF >= 6"];

NSArray *over6Array = [numbers filteredArrayUsingPredicate:over6Pred];

NSLog(@"over6Array = %@",over6Array);

// 最頻出のデータを取得

NSCountedSet *set = [NSCountedSet setWithArray:numbers];

int highest = 0;

NSNumber *mostFqtNumber = @0;

for (NSNumber *n in set) {

if ([set countForObject:n] > highest) {

highest = [set countForObject:n];

mostFqtNumber = n;

}

}

NSLog(@"most frequent number = %@",mostFqtNumber);