iOS--Quartz 2D

为了在一个ios应用上面绘制,应该先建立一个UIView对象,然后重写其drawRect:方法进行绘制。当view变的可见的时候或者其内容需要更新的时候,view的drawRect:方法会被调用。在调用自定义的drawRect:方法之前,view对象会自动的配置其绘制环境,然后代码可以立即的绘制。作为配置的一部分,UIView对象先为当前的绘制环境创建一个Graphics Context。我们可以在drawRect:方法中通过调用UIKit的UIGraphicsGetCurrentContext函数去得到此Graphics Context。

UIKit使用的缺省坐标系统与Quartz的坐标系统不相同。在UIKit中,原点在左上角。UIView对象通过translating 原点并且通过在y轴乘以-1来改变Quartz Graphics Context使其与UIKit相匹配。

下面是摘自iphone调参的DDProgressView类中的实现,该类是动画的进度条的实现。

- (void)drawBackgroundWithRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSaveGState(context);
    {
        // Draw the white shadow
        [[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.2] set];
        
        UIBezierPath* shadow        = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.5, 0, rect.size.width - 1, rect.size.height - 1)
                                                                 cornerRadius:_cornerRadius];
        [shadow stroke];
        
        // Draw the track
        [ProgressBarColorBackground set];
        
        UIBezierPath* roundedRect   = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, rect.size.width, rect.size.height-1) cornerRadius:_cornerRadius];
        [roundedRect fill];
        
        // Draw the inner glow,背景条目的底部效果,一根白色的线条,可以显示出凹凸的质感
        [ProgressBarColorBackgroundGlow set];
        
        CGMutablePathRef glow       = CGPathCreateMutable();
        CGPathMoveToPoint(glow, NULL, _cornerRadius, 0);
        CGPathAddLineToPoint(glow, NULL, rect.size.width - _cornerRadius, 0);
        CGContextAddPath(context, glow);
        CGContextDrawPath(context, kCGPathStroke);
        CGPathRelease(glow);
    }
    CGContextRestoreGState(context);
}

- (void)drawRect:(CGRect)rect
{
    //...

    // Draw the background track
    [self drawBackgroundWithRect:rect];
    
    //...
}

iOS--Run Loop

NSRunLoop 允许在您等待时响应事件runloop。如果你只是睡了(调用sleep函数),您的线程即使事件到达 (比如你在等待的网络响应)也无法响应。

在连续循环中,有可能会引起界面的停顿,可以考虑加入[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantPast]],其实就是本身的loop暂停一下,让主线程分点时间。

首先是Run Loop的部分概念,它的作用就是循环、处理事件。具体来说有两个方面:

在单线程的app中,不需要注意Run Loop,但不代表没有。程序启动时,系统已经在主线程中加入了Run Loop。它保证了我们的主线程在运行起来后,就处于一种“等待”的状态(而不像一些命令行程序一样运行一次就结束了),这个时候如果有接收到的事件(Timer的定时到了或是其他线程的消息),就会执行任务,否则就处于休眠状态。

如果我们要写多线程的程序,可能就需要自己来管理Run Loop。

    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]

RunMode: NSDefaultRunLoopMode,默认的run loop mode,可以把这个理解为一个”过滤器“,我们可以只对自己关心的事件进行监视。一般NSDefaultRunLoopMode是最常用的。

启动run loop的方法就是runMode:,它的说明是:Runs the loop once, blocking for input in the specified mode until a given date。启动run loop一次,在特定的run loop mode阻塞程序,直到有事件发生,或者设置的时间已经到期。blocking for input中input指的是需要响应的事件发生。

如果没有附加input source或是timer,或是时间过了limitDate,run loop就会退出阻塞状态,并且方法返回NO。

下来是Run Loop的使用场合:

run loop不需要创建,在线程中只需要调用[NSRunLoop currentRunLoop]就可以得到。

    // The hud will dispable all input on the view (use the higest view possible in the view hierarchy)
    self.HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
    [self.navigationController.view addSubview:self.HUD];
    [self.HUD show:YES];
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantPast]];
    
    //登陆
    NSNumber* loginResult = [[LoginRegist sharedInstance] Login:userName password:password];
    [self.HUD hide:YES];

假设我们想要等待某个异步方法的回调。比如connection。如果我们的线程中没有启动run loop,是不会有效果的(因为线程已经运行完毕,正常退出了)。我们可以用一个条件来运行run loop。

这样就可以一直进行等待,直到在别的位置将done置为YES,表示任务完成。

-(void)start
{
    isFinished  = NO;

    ...
    while(isFinished != YES) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
}
while(done)
{
    [ NSRunLoop currentRunLoop]  runMode:currentMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}

上面这段话看似程序进入了死循环,其实并不是这样。这段程序的意思是:

如果当前线程有当前设置的runMode下的事件发生,runloop就会启动,处理对应的事件。如果没有事件发生,runloop就会每过10秒钟启动一次当前线程的runloop.

如果runloop每次启动成功 [ NSRunLoop currentRunLoop] runMode:currentMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; 返回值为YES,这个启动成功包括了时间触发和10秒钟到了之后触发两种情况。如果启动失败返回false.

说到这个地方可能还不明白,为什么要搞个循环,为什么要用runloop,我刚开始的时候也是搞得不太明白。

这个地方解释一下:

当我们在ios设备上面触摸屏幕之后,对应的tounch事件就会调用,这是为什么呢,其实这个地方就有runloop的功劳。

其实runloop做为一种时间处理机制,类似一个车间的主任(不知道这种比喻是否恰当),这个主任他负责处理这个车间流水线上面发生特定类型事件的处理(这里的特定事件就是runMode)。这个事件可以包括安全事件,机械事件等等。该主任处理事件的传统方式可以是每隔一分钟巡逻生产线一次(对应的是cpu空转轮询消息队列的方式),这种方式比较耗费工人体力(cpu资源,电量),当发现有问题发生,他就找对应的工人去处理,这里的工人对应于时间的处理函数;还有一种方式就是主任平时都在睡觉打麻将,当生产线发生问题的时候,如果是属于他的职责,系统就直接给他发送一条短信通知他,他收到之后再通知对应的工人去处理,上面那个十秒钟(这个是可以更改的)就是如果十秒内没有消息通知过来,主任才会去车间巡逻一次,但是这十秒由于主任是没有收到事件处理消息的,所以他通常是到了车间就走了(对应runLoop启动就结束,没有事件处理)。

因此采用runloop的好处就显而易见了。

runloop处理的事件包括:

如果在执行周期性工作的时候就可以采用上面那种,用一个循环,当时间回调的时候(这个地方的一个典型应用场景就是处理网络数据,网络有数据返回就启动一次runloop,直到所有数据处理完成之后将done设置为YES。结束);

iOS--其他

NSBundle与文件资源

获取app下的Error.html文件路径。

    path = [[NSBundle mainBundle] pathForResource:@"Error" ofType:@"html"];

NSLocale language

判断当前的系统语言设置是否为中文简体。


    if ([[[NSLocale preferredLanguages] objectAtIndex:0] isEqualToString:@"zh-Hans"]) {
        //中文用户
        path = [[NSBundle mainBundle] pathForResource:@"Error" ofType:@"html"];
    }
    else
    {
        //其他语言用户,都配置为英文
        path = [[NSBundle mainBundle] pathForResource:@"Error_en" ofType:@"html"];
    }

amazon库中try catch的使用


+ (NSMutableArray *)getFacets:(NSString *)withName
{
  @try 
  {
    AmazonS3Client * s3 = 
      [[AmazonS3Client alloc] 
      initWithAccessKey:ACCESS_KEY_ID withSecretKey:SECRET_KEY];
 
    S3GetObjectRequest * request = 
      [[S3GetObjectRequest alloc] 
      initWithKey:withName withBucket:MODEL_BUCKET];   
 
    S3GetObjectResponse * response = [s3 getObject:request];
 
    NSData * data = [response body];
 
    // Convert it to list of points
    return [self getFacetsFromData:data];
  }
  @catch (AmazonClientException * exception) 
  {
    [self showAlert:exception.message withTitle:@"Download Error"];
  }
 
  return [[NSMutableArray alloc] init];
}

定时器循环


NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:@selector(scanMainControllerAssistantCmd) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
[timer fire];
[timer performSelector:@selector(invalidate) withObject:nil afterDelay:5.0f];

performSelector和respondsToSelector

performSelector函数用于对象调用它自己的方法。

//self这个对象调用它的方法_onScanMainCtrlTimer。如果改方法不存在,则会报错
[self performSelector:@selector(_onScanMainCtrlTimer:)];

对于delegate的实现时,也可以不使用performSelector函数。

    [self.delegate ReconnectFinished:YES];

如果delegate是optional,需要respondsToSelector函数先判断其是否存在该这样的方法。

if ([self.delegate respondsToSelector:@selector(didScanMainControllerPeripheral:)]) {
    [self.delegate performSelector:@selector(didScanMainControllerPeripheral:) withObject:mainControllerPeripheral];
}

no appropriate for no-gc object

警告信息:“No 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed”和“Default property attribute 'assign' not appropriate for non-gc object”。警告信息的意思是:“没有明确指出应该是assign还是retain或者是copy,却省的是assign”和“缺省得属性设置assign不适合非gc对象 ”

ios程序基本的起始代码

self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.

self.listViewController = [[BLEListViewController alloc] initWithStyle:UITableViewStyleGrouped];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.listViewController];

self.window.rootViewController = self.navigationController;
//[self.window addSubview:[self.navigationController view]];
[self.window makeKeyAndVisible];

nil指针被调用不会报错,区别于野指针

//如果不存在该成员,则返回的值为nil,后面的调用也不会报错
NSMutableArray * activeMainCtrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"activeMainCtrl"];
[activeMainCtrl addObject:infoItem];

NSLog(@"[001] saved mc : %@",activeMainCtrl);

阻塞线程,sleep

[NSThread sleepForTimeInterval:30];

http get请求

    //登陆
    NSError *error;
    
    NSString *baseurl = [NSString stringWithFormat:@"http://test.skypedia.net/app/signin?user=%@&password=%@",yourEmail.inputField.text,password.inputField.text];
    
    NSLog(@"base url: %@",baseurl);
    
    NSURL *url = [NSURL URLWithString:baseurl];
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    [request setHTTPMethod:@"GET"];
    [request setTimeoutInterval:8];
    NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
    
    if (!returnData) {
        NSLog(@"login failed: %@",[error localizedDescription]);
        
        UIAlertView* alter = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"login failed" ,@"") message:[error localizedDescription] delegate:self cancelButtonTitle:NSLocalizedString(@"OK" ,@"") otherButtonTitles:nil];
        [alter show];
        [alter release];
        return;
        
    }
    else{
        NSString *returnMsg = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
        NSLog(@"return msg %@",returnMsg);
        
        //解析返回信息
        if ([returnMsg hasPrefix:@"0 "]) {
            //登陆成功,修改默认登陆名和密码
            [[NSUserDefaults standardUserDefaults] setObject:yourEmail.inputField.text forKey:@"UserName"];
            [[NSUserDefaults standardUserDefaults] setObject:password.inputField.text forKey:@"password"];
            [[NSUserDefaults standardUserDefaults] synchronize];
            
            NSLog(@"Login action finish");
        }
        else{
            UIAlertView* alter = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"login failed" ,@"") message:returnMsg delegate:self cancelButtonTitle:NSLocalizedString(@"OK" ,@"") otherButtonTitles:nil];
            [alter show];
            [alter release];
        }
    }

列举目录下的文件

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    
    NSLog(@"paths: %@",paths);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSLog(@"documentsDirectory: %@",documentsDirectory);
    NSFileManager *fileManage = [NSFileManager defaultManager];
    NSArray *file = [fileManage subpathsOfDirectoryAtPath: documentsDirectory error:nil];
    NSLog(@"%@",file);
    NSArray *files = [fileManage subpathsAtPath: documentsDirectory ];
    NSLog(@"%@",files);

document目录

NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,                                                        NSUserDomainMask, YES) objectAtIndex:0];
    
NSLog(@"rootPath %@",rootPath);

NSString *path =  [[NSBundle mainBundle] pathForResource:@"Config" ofType:@"plist"];
NSLog(@"path: %@",path);

结果:rootPath /var/mobile/Applications/D14CF626-40EF-484D-A94D-72C649A483FD/Documents

结果:2012-12-15 14:03:50.236 AssistantForIphone[15065:907] path: /var/mobile/Applications/D14CF626-40EF-484D-A94D-72C649A483FD/AssistantForIphone.app/Config.plist

app跳转到系统程序

app跳转到浏览器,并且打开链接

#define FORGET_PASSWORD_URL     @"http://login.dji-innovations.com/member/forget/en_US"
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:FORGET_PASSWORD_URL]];

app跳转到email,并且设置收件人

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto://app@dji-innovations.com"]];