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 標籤是否有用..)