iOS编程细节整理

关于iOS开发中遇到的一些小问题及其解决方法

tableView不全屏问题

cell.separatorInset = UIEdgeInsetsMake(0, -100, 0, 0);

解决有的图片显示时只是一块颜色,而显示不正常的问题

//渲染
UIImage *image = [UIImage imageNamed:@"imageName"];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];

毛玻璃效果

 UIImageView *imageView = [[UIImageView     alloc]initWithImage:[UIImage imageNamed:@"xiaoxin3"]];
//毛玻璃效果
UIVisualEffectView *visualView = [[UIVisualEffectView alloc]initWithEffect:[UIBlurEffect effectWithStyle:(UIBlurEffectStyleLight)]];
visualView.frame = self.tableView.bounds;
visualView.alpha = 0.8;
[imageView addSubview:visualView];

self.tableView.backgroundView = imageView;

视图截圆角

_customView.layer.masksToBounds = YES;
_customView.layer.cornerRadius =     self.customView.bounds.size.width/2;   

使用NSURLSession请求网络数据

 //通过单例创建Session对象
//步骤1.NSURLSession 服务器数据异步加载,作用和NSURLConnection的作用相同
NSURLSession *session = [NSURLSession sharedSession];
//步骤2.封装网络请求
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"baidu.com"]];
//步骤3.准备加载数据,创建这个任务的task
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    //当加载数据完成时,调用该block
    NSLog(@"%@",data);

    //手动解析网络数据
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];

    NSLog(@"%@",data);
}];

//调用此方法开始加载数据
[task resume];

播放语音

//导入包
import <AVFoundation/AVSpeechSynthesis.h>
//声音集成器
AVSpeechSynthesizer *speechSy = [[AVSpeechSynthesizer alloc] init];
//发声器
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:@"i am very happy"];

AVSpeechUtterance *utterance2 = [[AVSpeechUtterance alloc] initWithString:@"ha ha ha"];
AVSpeechUtterance *utterance3 = [[AVSpeechUtterance alloc] initWithString:_textFeild.text];

//给合成器添加发生器,让其发音
[speechSy speakUtterance:utterance];
[speechSy speakUtterance:utterance2];
[speechSy speakUtterance:utterance3];

//哪国语言
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"];
//语速
utterance.rate = 0.5;
//音高
utterance.pitchMultiplier = 1.0;

webView

 UIWebView *webView = [[UIWebView alloc]  initWithFrame:self.view.frame];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:myurl]]];
self.view = webView; 

使用第三方SDWevImage解析图片

[cell.imgView sd_setImageWithURL:[NSURL URLWithString:news.picUrl] placeholderImage:[UIImage imageNamed:@"image"]];

设置cell的自适应高度

使用此方法必须要设置cell中的最后一个控件与cell的距离

self.myTableView.rowHeight =   UITableViewAutomaticDimension;
self.myTableView.estimatedRowHeight = 100;

让音乐在后台播放

[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayback error:nil];     

将URL中的汉字转换成utf-8格式,拼接到URL字符串中

NSString *typeString = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,(__bridge CFStringRef)@"健康",NULL,(CFStringRef)@"!*'();:@&=+$,/?%#[]",kCFStringEncodingUTF8);    

去除解析出来的数据带的 HTML 标记

- (NSString *)filterHTML:(NSString *)html{
NSScanner * scanner = [NSScanner scannerWithString:html];
NSString * text = nil;
while([scanner isAtEnd]==NO)
{
    //找到标签的起始位置
    [scanner scanUpToString:@"<" intoString:nil];
    //找到标签的结束位置
    [scanner scanUpToString:@">" intoString:&text];
    //替换字符
    html = [html stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%@>",text] withString:@""];
}

return html;
}    

去广告

先到网页,找到开发者平台,依次输入并提交

document.getElementsByClassName(‘index_mask’) ->
document.getElementsByClassName(‘index_mask’)[0] ->
document.getElementsByClassName(‘index_mask’[0].style.display = ‘none’

若在网页上显示的对应的广告没了,就可以将最后一句写到下面的程序中

-(void)webViewDidFinishLoad:(UIWebView *)webView{
[webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByClassName('index_mask')[0].style.display = 'none'"];
}

图片自适应

imgView.contentMode = UIViewContentModeScaleAspectFit;

解决tableView的head不会和cell一起滚动的问题

self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0,self.view.frame.size.width, self.view.frame.size.height) style: UITableViewStyleGrouped];

解决有navigationbar的情况下,下面的tableView与navigationbar有一段距离的问题

self.automaticallyAdjustsScrollViewInsets = NO;

数据再cell中解析,给cell做自适应高度时,无法实现

可能是因为cell出现时,自适应高度已经实现了,但是还没有内容,所以想办法先刷新一下数据(使用block块可以在cellForRow……方法中实现)

如何获取UIWebView中Button事件

//通过在网页中添加js脚本函数,当button单击时触发该函数
//在UIWebView中会自动回调代理方法
//shouldStartLoadWithRequest:navigationType

批量上传和下载实现

//网页的实现方式
//mutipart-formdata post表单
//iOS的实现方式
//通过在线程技术中完成批量上传的功能

如何使用SDWebImage中的方法实现实时个人头像更新

//通过修改个人信息Model类中的个人头像字符串
//因为SDWebImage中存储图片时的原理,是将url字符串进行md5加密后作为iOS本地缓存文件的文件名存储的
//所以当,个人头像没有发生改变时,即个人头像的url字符串没有发生改变,当个人头像发生改变时,个人信息Model类中的url同时也发生变化,就好了

极光推送的思路

![http://docs.jpush.io/guideline/ios_guide/]()

股票K线图?画线?

![https://github.com/zhiyu/chartee]()

给一个NSDate对象,获取到一年中的第几个月中的第几天

NSDate *date = [NSDate date];
//获取对应属性
NSDateComponents *comp = [[NSCalendar currentCalendar] componentsInTimeZone:[NSTimeZone defaultTimeZone] fromDate:date];
/*
 @interface NSDateComponents : NSObject <NSCopying, NSSecureCoding>
 @property (nullable, copy) NSCalendar *calendar NS_AVAILABLE(10_7, 4_0);
 @property (nullable, copy) NSTimeZone *timeZone NS_AVAILABLE(10_7, 4_0);
 @property NSInteger era;
 @property NSInteger year;
 @property NSInteger month;
 @property NSInteger day;
 @property NSInteger hour;
 @property NSInteger minute;
 @property NSInteger second;
 @property NSInteger nanosecond NS_AVAILABLE(10_7, 5_0);
 @property NSInteger weekday;
 @property NSInteger weekdayOrdinal;
 @property NSInteger quarter NS_AVAILABLE(10_6, 4_0);
 @property NSInteger weekOfMonth NS_AVAILABLE(10_7, 5_0);
 @property NSInteger weekOfYear NS_AVAILABLE(10_7, 5_0);
 @property NSInteger yearForWeekOfYear NS_AVAILABLE(10_7, 5_0);
 @property (getter=isLeapMonth) BOOL leapMonth NS_AVAILABLE(10_8, 6_0);
 @property (nullable, readonly, copy) NSDate *date NS_AVAILABLE(10_7, 4_0);
 */
//获取一年中的第几天
//小范围参数:NSCalendarUnitDay
//大范围参数:NSCalendarUnitYear
/*
 NSCalendarUnitEra = kCFCalendarUnitEra,
 NSCalendarUnitYear = kCFCalendarUnitYear,
 NSCalendarUnitMonth = kCFCalendarUnitMonth,
 NSCalendarUnitDay = kCFCalendarUnitDay,
 NSCalendarUnitHour = kCFCalendarUnitHour,
 NSCalendarUnitMinute = kCFCalendarUnitMinute,
 NSCalendarUnitSecond = kCFCalendarUnitSecond,
 NSCalendarUnitWeekday = kCFCalendarUnitWeekday,
 NSCalendarUnitWeekdayOrdinal = kCFCalendarUnitWeekdayOrdinal,
 */
NSInteger day = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitYear forDate:date];
NSLog(@"%ld",day);

runtime的实现原理

链接: http://pan.baidu.com/s/1i4ijIsP 密码: k661

tableView滑动时卡顿的处理办法

//一般遇到卡顿时候,会出现的原因
//1、在单元格中使用了同步下载图片,解决方法是使用多线程技术
//2、在单元格中自定义高度布局时,进行了大量的计算,解决方法是使用缓存技术,在下载完数据后,自动生成一个对象应该所占的高度

一.内存管理情况

  • 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象。当用户的代码运行告一段 落,开始等待用户的操作,自动释放池就会被释放掉(调用dealloc),池中的对象都会收到一个release,有可能会因此被销毁。
  • 2-成员属性:
    readonly:不指定readonly,默认合成getter和setter方法。外界毫不关心的成员,则不要设置任何属性,这样封装能增加代码的独立性和安全性。相当于私有成员?
    assign:表示只进行简单的赋值,不会发送retain消息。
    retain:会向旧值发送release消息,向新值发送retain消息。
    copy:向旧值发release消息,向新值发送copy消息。
    3-引用计数特殊情况:
    NSArray:当把一个对象添加到NSArray时,对象的引用计数会加1,移除时会减1。
    UIView:addGestureRecognizer会对手势识别器的引用计数加1,removeGestureRecognizer会减1。

二 .图形API

这个讲的好,清晰明确:图形API

  • 1-Quartz2D,的绘图总是发生在图形环境(Graphics Context)中。视图会在调用drawRect:方法进行绘图之前,创建好图形环境,
    通过UIGraphicsGetCurrentContext函数来获得这个图形环境。
    如果直接在图像或者PDF上绘图,需要调用CGBitmapContextCreate或者CGPDFContextCreate函数来创建图形环境。
    Quartz2D绘图的基础元素是路径。路径可以是一些基础几何形状,也可以是这些几何形状的组合。
    当需要创建一条路径时,应当调用CGContextBeginPath函数;
    当需要将路径绘制的起点移动到一个位置时,应当调用CGContextMoveToPoint函数;
    当想绘制一条线段时,应当调用CGContextAddLineToPoint。 CTM(当前变换矩阵)将绘图从用户空间映射到设备控件。
    当图形环境刚刚创建时,CTM初始化为一个单位矩阵。
    对CTM进行平移变换应当调用CGContextTranslateCTM函数,进行旋转变换应当调用CGContextRotateCTM函数,进行缩放变换应当调用CGContextScaleCTM函数。

  • 2-UIBezierPath
    使用UIBezierPath绘图的好处:

    • 采用系统坐标系
    • 在Core Graphics的基础上封装,具有Core Graphics的主要功能,无需考虑图形环境。

      UIBezierPath* pathLines = [UIBezierPath bezierPath];
      [pathLines moveToPoint:point1]; // 移动到point1位置
      [pathLines addLineToPoint:point2]; // 画一条从point1到point2的线
      pathLines.lineWidth = 5.0; // 线宽
      [UIColor redColor] set]; // 颜色设置
      [pathLines stroke]; // 开始描绘
      在同一UIBazierPath中,只能采用相同的颜色和线宽。
      可以调用closePath,从当前点画一条直线到当前子路径的初始点。如果要填充路径,应当使用fill方法,此方法会填充路径中得所有封闭子路径。

三.容器

-NSArray和NSMutableArray
+array:创建一个空数组
+arrayWithArray:从另一个数组创建新的数组
+arrayWithContentsOfFile:读文件创建数组
+arrayWithObject:创建一个数组,其中包含一个给定对象
+arrayWithObjects
+arrayWithObjects:count: 从C数组创建
-containsObject:是否包含一个元素
-count:数量
-lastObject:返回最后一个
-objectAtIndex:返回某一个
-objectsAtIndexes:返回一组,类型为NSArray
-indexOfObject:返回对象索引
-arrayByAddingObject:原数组最后加一个对象,产生一个新的数组
-arrayByAddingObjectsFromArray:在原数组的最后添加另一个数组中的所有对象,产生一个新数组
-subarrayWithRange:抽取原数组中得一部分,产生一个新的数组
-isEqualToArray:比较两个数组是否相同
-writeToFile:atomically:保存数组至一个文件
-writeToURL:atomically:保存数组至一个URL
-addObject:在数组最后添加一个对象
-addObjectsFromArray:在原数组最后添加另一个数组的全部对象
-insertObject:atIndex:向原数组制定位置添加一个对象
-insertObjects:atIndexes:向原数组中一系列位置添加一系列对象
-removeAllObjects:移除数组中的全部对象
-removeLastObject:移除数组中最后一个对象
-removeObject:移除一个对象
-removeObjectAtIndex:移除位于指定位置的对象
-removeObjectsAtIndexes:移除位于一系列位置的对象
-replaceObjectAtIndex:withObject用给定对象替换位于指定位置的对象
-replaceObjectsAtIndexes:withObjects:多对象,多位置版本
-setArray:用另一个数组中的所有对象来替换当前数组中的所有对象
-NSDictionary与NSMutableDictionary
+dictionary
+dictionaryWithContentsOfFile
+dictionaryWithContentsOfURL
+dictionaryWithDictionary
+dictionaryWithObject:forKey
+dictionaryWithObjects:forKeys
+dictionaryWithObjectsAndKeys
-count:数量
-allKeys:返回一个数组,包含字典中的所有关键字。
-allKeysForObject:返回一个数组,包含所有对应到给定对象的关键字。
-valueForKey:通过字符串查找数值。
-writeToFile:atomically
-writeToURL:atomically
-setValue:forKey: 加键-值对
-addEntriesFromDictionary: 添加另一个字典中得所有条目
-setDictionary:将原字典中条目设置为另一个字典中得所有条目
-removeObjectForKey:移除一个关键字的对应条目
-removeAllObjects:移除所有条目
-removeObjectsForKeys:移除一系列关键字的对应条目

四.文件系统

  • 1- 概述
    bundle(沙盒):一个应用只能在自己的沙盒中读取或存储文件,而不能操作其他沙盒中的文件。好处:保护应用的隐私不受侵犯,保护系统文件的安排,应用删除后相关文件也能被全部清除。
    每个应用都有自己的沙盒,沙盒的根目录的获取方法是NSHomeDirectory()。
    根目录下的内容:
    -.app文件,实际上是一个文件夹,包含了可执行文件、Nib文件、图片资源、plist等。XCode中能看到的资源,及编译后的可执行文件,都封装在app中。
    -Documents文件夹,用于存放你的应用所产生的数据,该文件夹可通过iTunes备份,可以存储游戏进度等。
    -Library文件夹,用于存放用户偏好和临时文件。
    -tmp文件夹是系统的中转站。
  • 2-文件管理器
    NSFileManagerdefaultManager()返回一个文件管理器的单例(多线程下不安全)。init(),在多线程编程中应尽量使用init()。

代理方法:-fileManager:shouldRemoveItemAtPath和-fileManager:shouldRemoveItemAtURL在移除操作之前被调用。

-removeItemAtPath:error:删除位于指定路径的文件、连接、目录(及其所有子目录、文件)。
-removeItemAtURL:error:同上。

-contentOfDirectoryAtPath:查找所有位于给定路径的子路径和文件。返回值为一个数组,其中包含了NSString对象。查找只在当前目录进行,不会进入下一层目录。

-subpathsAtPath:查找给定路径下的所有子路径。深度查找,不限于当前层,也会查找package的内容。

-fileExistsAtPath:判断文件是否位于一个路径下面。

-isReadableFileAtPath:查询文件的可读性

-isWritableFileAtPath:可写性

-isExecutableFileAtPath:查询文件的可执行性

-isDeletableFileAtPath:可删除性

  • 3-NSString的路径功能

    -pathWithComponent:参数是一堆components构成的数组,返回的路径是由这些components连接而成的路径字符串,相邻components之间用/隔开。
    -pathComponents:返回一个数组,包含路径中的components。
    -fileSystemRepresentation:返回C字符串
    -isAbsolutePath:判断是否为绝对路径
    -pathExtension:返回文件的扩展名,没有的就返回空字符串
    -stringByAppendingPathComponents :向现有路径添加一个component。斜杠/会被自动加上
    -stringByAppendingPathExtension:向现有路径加上文件的扩展名
    -stringByDeletingLastPathComponent:移除最后一个路径component
    -stringByDeletingPathExtension:删除路径扩展名
    -stringByAppendingPaths:参数为一个数组,此方法将数组中的字符串对象作为路径一次添加到源字符串后面。
    

    例子:

    NSString homePath = NSHomeDirectory();
    NSString docPath = [homePath stringByAppendingFormat:@"/Documents"];
    

五.UIAlertController

UIAlertController * alterVC = [UIAlertController alertControllerWithTitle:@"标题" message:alter preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * veryfiAction  = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {      
    NSLog(@"确认");
}]; 
[alterVC addAction:veryfiAction];
[self.window.rootViewController presentViewController:alterVC animated:YES completion:nil];

六.旋转

  • 1- interfaceOrientation属性查询当前的界面方向。
  • 2- rotatingHeaderView方法来查询当前导航视图。
  • 3- rotatingFooterView来查询当前的标签条。

    七.快速拨打电话接口

    openURL能帮助你运行Maps,SMS,Browser,Phone甚至其他的应用程 序。这是Iphone开发中我经常需要用到的一段代码,它仅仅只有一行而已。
[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel://8004664411"]; 

这个程序通过基础的协议支持拨打电话的功能。

八.区分设备类型的方法

打电话功能只有iPhone支持,对于其他设备对应按钮应该禁用。

用[UIDevice currentDevice].model,这个返回的是一个NSString,你可以做如下判断就能知道设备是iPad还是iPhone.

if ([UIDevice currentDevice].model            
 rangeOfString:@"iPad"].location != NSNotFound) { 
  NSLog(@"This is an iPad!"); 

}
用UI_USER_INTERFACE_IDIOM()方法,这是系统定义的一条宏。使用方法也很简单。

 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)      
{ 
 NSLog(@"This is an iPad!"); 
 }

九.并发编程

  • 1-GCD-Grand Central Dispatch,GCD使用队列来管理多个任务,而每个任务是以块(其定义类似于函数)来描述的。GCD从队列中读取这些要完成的任务,然后其多核执行 引擎负责将这些任务分配给它所管理的线程。和手动线程管理相比,GCD能极大地优化线程管理,并极大地减少创建和销毁线程所带来的额外开销。除了在多核系 统上得性能改进之外,GCD还能帮助提高应用的响应速度,并让代码更为干净,易于维护。
  • 2-NSOperationQueue-操作队列,可以极大地减少开发者在并发编程中的负担,并且操作队列有一套Objective-C的API,使用起来方便。
    基本操作单位-NSOperation,操作。
    操作是封装了一个与任务有关的代码与数据的抽象类,需要继承NSOperation,并且描述相关的任务。操作对象是一次性的,它只能执行一次,下一次,需要创建一个新的实例。
    可以通过addDependency:方法为操作添加相互依赖性。假设操作A依赖于操作B,如果操作B没有结束,操作A就不会开始执行。完成是指 执行完,或者取消了操作。有时需要对操作是否成功作出判断。通过removeDependency:方法来移除操作的相互依赖性。
    操作的一个属性是isConcurrent。当使用操作队列来管理这些操作时,isConcurrent应当设置为NO,这时应当重载 NSOperation的main函数(在类中保存controller对象-weak,并在main中调用controller的方法)。 isReady属性用于描述操作是否已经可以开始执行,isExecuting属性表示操作是否正在执行当中,isFinished属性表示操作是否顺利 完成,isCancelled属性则表示操作已经取消。
    可以在操作运行中取消它的运行,只需要给它发送cancel消息即可。在继承NSOperation类时,也需要支持cancel方法。
    如果只需要简单的操作,不想继承NSOperation类,可以使用NSInvocationOperation类,这个类中的-initWithTarget:selector:object:方法能帮助你方便地选择人物的对象和相应的功能。
    操作的执行顺序取决于各自的优先级和相互之间的依赖性。
    操作队列有两种方式来执行其中的操作。第一种是开辟新线程来运行这些操作,第二种则是通过GCD来运行这些操作。但是无论如何,都不需要手动管理 线程。尽量使用操作队列,只有在操作队列的效率明显不足时才转向GCD。因为操作队列是封装在GCD的基础之上的,虽然增加了一些额外开销,但是也具有更 加完善的功能。例子是TestNSOperation

十.忙碌指示器

UIActivityIndicator。
UIActivityIndicatorView *activityIndicatior = [UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge]; 
activityIndicator.center = CGPointMake(512, 384); 
[self.view addSubview: activityIndicator]; 
[activityIndicator startAnimating];  
[activityIndicator stopAnimating]; 
[activityIndicator removeFromSuperView];

十一.媒体播放

  • 1- 音频
    [1] 音乐往往是存储在iPod曲库(注意位置)中的,可以通过媒体选择器media picker或者媒体查询media query读取,然后用音乐播放器MPMusicPlayerController播放。
MPMusicPlayerController *musicPlayer = [MPMusicPlayerController applicationMusicPlayer]; 
[musicPlayer setShufleMode: MPMusicShuffleModeSongs]; 
[musicPlayer setRepeatMode: MPMusicRepeatModeAll]; 
[musicPlayer setQueueWithQuery: [MPMediaQuery songsQuery]; 
[musicPlayer play];

applicationMusicPlayer返回的播放器,在你的应用中播放音乐。它不会影响到iPod播放器,也不能从iPod播放器重获取信息。
iPodMusicPlayer返回的是iPod播放器,在你推出应用后,所有的设置都会影响到之后设备上的iPod播放器。
获得音乐播放器后,需要为它设置一个播放队列。可以用setQueueWithQuery:放方法,通过媒体查询MPMediaQuery来设置 播放队列,也可以用setQueueWithItemCollection:方法,通过MPMdiaItemCollection来设置播放队列。
重复模式repeatMode可以设置为不重复、重复当前曲目、或整个播放列表;乱序播放shuffleMode可以设置为不乱序、乱序播放曲目或乱序播放专辑;音量volume的设置与音频播放器一样。
skipToNextItem跳到下一首,skipToPreviousItem跳到上一首,skipToBegin跳到第一首。
对应的宏都是以MPMusic开头。

[2] 利用系统声音服务来播放短暂音效(时长30秒以内),并震动:

AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);  
播放指定音效:
NSURL *fileURL = [NSURL fileURLWithPath: path isDirectory: NO];  
// 创建音效ID 
SystemSoundID soundID; 
AudioServiceCreateSystemSoundID((CFURLRef) fileURL, &soundID); 
// 播放声音 
AudioServicesPlaySystemSound(soundID);

[3] 音频播放器

没有时长限制

NSURL *fileURL = [NSURL fileURLWithPath: path isDirectory: NO]; 
// 用URL来初始化音频播放器-播放的不是iPod曲库中的音乐 
AVAudioPlayer* player = [AVAudioPlayer alloc] initWithContentsOfURL: fileURL error: NO]; 
// 准备播放 
[player prepareToPlay]; 
// 设置代理 
[player setDelegate: self]; 
方法:play、pause、stop。可以通过playing属性查询播放器是否正在播放当中,可以通过volume属性来修改和查询播放器的播放增益(从0.0到1.0),可通过setting属性查询播放器其他设置。

duration表示音频的时间长度, currentTime表示当前播放到的时间。播放结束后可以通过代理方法audioPlayerDidFinishPlaying:来处理播放后设置。
  • 2- 视频
    视频播放可以采用网页视图UIWebView进行嵌入式播放(能播放YouTube视频),或者采用电影播放器MPMoviePlayerController进行播放。
    [1] 电影播放器
MPMoviePlayerController *player =     [MPMoviePlayerController alloc]initWithContentURL: url]; 
// 设置播放器的大小,并将其加入视图中 
[player.view setFrame: rectFrame]; 
[self.view addSubView: player.view]; 
播放器的背景视图backgroundView。 
全屏[player setFullscreen: YES animated: YES]; 
播放另一个影片[player setContentURL: newURL]; 
[player requestThumbnailImagesAtTimes:arrayTimes timeOption:MPMovieTimeOptionNearestKeyFrame]; // 表示播放器不会在你所指定的时间去截取预览,而是在绝对时间的附近几帧中寻找效果最好的帧做为预览。 

scalingMode规定了影片的缩放模式。
initialPlaybackTime用来控制视频开始播放的时间,单位是秒。
如果视频源在网络上,那么需要正确设置服务器端的mimeType

十二.运行环-runloop

运行环作用于一个iOS应用的整个生命周期。它负责监视各种输入事件,并且在合适的时候对这些输入进行分配。应用的每一个线程都有且仅有一个运行环。你自己不需要创建也不需要销毁运行环,但是可以通过currentRunLoop方法来获取当前的运行环。

十三.定时器

由于运行环机制,定时器的精度不高,只能用于一般性延时。
例子:拼图游戏,DeskViewController.m。
NSObject类的定时方法。

performSelector: withObject: afterDelay: 运行方法,参数,时间(秒)。
performSelectorOnMainThread: withObject: waitUntilDone: 在主线程中,运行参数selector所指定的方法,如果waitUntilDone参数为YES,那么当前线程会被阻拦,直到selector运行完。
performSelector: onThread: withObject: waitUntilDone:同上,但不一定在主线程中运行。
performSelectorInBackground: withObject: 开启一个新线程,用于运行selector方法,selector方法应负责线程的初始化。
cancelPreviousPerformRequestsWithTarget:取消与一个目标相关的所有计划好的动作。
cancelPreviousPerformRequestsWithTraget: selector: object只取消特定的计划动作。

十四.随机数

0~1之间随机数

CG_INLINE float genRandomNum() 
{ 
return (float)arc4random/ARCRANDOM_MAX; 
}

十五.地图与连接服务器

[1] 添加框架MapKit.framework。使用MKMapView来呈现地图。注意应当直接使用此类,而不是继承之。如果希望在MKMapView类之上添加功能,可以使用MKMapViewDelegate协议。

初始化:

MKMapView *mapView = [MKMapView alloc] initWithFrame: rect]; 
初始化之后并不直接显示,还需要指定显示的地图区域:
CLLocationCoordinate2D coordinate; 
coordinate.latitude = latitudeValue; // 纬度 
coordinate.longtitude = longtitudeValue; // 精度 
// 指定显示区域,width和height单位都是米 
mapView.region = MKCoordinateRegionMakeWithDistance(coordinate, width, height); 
之后可以通过addSubview添加地图。

属性:showsUserLocation-为YES,系统会持续跟踪用户的位置
userLocationVisible-为YES,将显示用户所在位置
显示地图之后,常常希望在地图上添加标注,这需要创建一个类,并实现MKAnnotation协议,这个类叫做标注对象。标注对象往往实现 setCoordinate:方法来设置其坐标。在地图视图上,可以设置标注对象的坐标,然后添加进去,这样地图上就会出现一个标注。代理方法title 和subtitle能够在标注上显示标题和副标题。

// 初始化 
   mapView = [MKMapView alloc] initWithFrame: CGRectMake(100, 100, 550, 700)]; 
   mapView.showsUserLocation = TRUE; 
   mapView.mapType = MKMapTypeStandard; 
   mapView.delegate = self; 
   // 设置坐标 
   CLLocationCoordinate2D coordinate; 
   coordinate.latitude = 37.31; 
   coordinate.longtitude = -122.03; 
   mapView.region = MKCoordinateRegionMakeWithDistance(coordinate, 4000, 6000); // 4000米宽,6000米高的区域 
   [self.view insertSubview: mapView atIndex: 0];

十六.通过storyboard来初始化一个controller

CBigDesignImageViewController *imageViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"BigImageController"];  
   BigImageController是要在storyboard中设置的Identifier属性。

十七.给UIView子类设置阴影,包括各种控件

  • 1 导入QuartzCore framework
  • 2 #import
  • 3 编码:

    [imgView layer] setShadowOffset:CGSizeMake(5, 5)]; // 阴影的范围  
    [imgView layer] setShadowRadius:2]; // 阴影扩散的范围控制  
    [imgView layer] setShadowOpacity:1]; // 阴影透明度  
    // 阴影的颜色     
    [imgView layer] setShadowColor:[UIColor brownColor].CGColor];
    

    十八.设置UIScrollView滚动速度

// 自动滚动太快,效果不好,这里把动画设置慢点,注意下面要直接赋值contentOffset,不要用带animated参数的函数,否则动画会出问题,因为两处都是动画效果。   
  [UIScrollView animateWithDuration:1.0f  
                       delay:0  
                       options:UIViewAnimationCurveLinear 
                       animations:^{ 
                           scrollView.contentOffset = CGPointMake(0, 0);  
                       } 
                       completion:^(BOOL finished){} 
                       ];  
// 如果在减速滚动过程中,按了刷新按钮,执行上面的动画,会出现重置的位置,y不是0的情况,这里再调用一次,滚动到0。 
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES];

十九.EXC_BAD_ACCESS

Here’s the advice I generally give to developers when you hit an EXC_BAD_ACCESS error:
  • Set the NSZombieEnabled argument in your executable options, which sometimes helps narrow down the cause
  • Run with Apple Instruments such as Leaks to look for memory issues
  • Set a breakpoint in your code and step through until you narrow down where it’s crashing
    Tried and true “comment out code till it works” then backtrack from there :]
    设置 NSZombieEnabled 的方法:
    你可以点击 Xcode 菜单 Product -> Edit Scheme -> Arguments, 然后将点击”加号”, 将 NSZombieEnabled 参数加到 Environment Variables 窗口中, 后面的数值写上 ”YES”.
    或者在 Xcode4 菜单 Product -> Edit Scheme -> Diagnostics 设置窗口中直接勾上 Enable Zombie Objects 即可,Xcode 可用 cmd+shift+< 进到这个窗口。

二十 iOS 8 及 iOS 9

定位
在IOS8中定位功能新增了两个方法:

- (void)requestWhenInUseAuthorization __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_8_0);

- (void)requestAlwaysAuthorization __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_8_0);

这两个新增的方法导致,之前写的程序在iOS8运行会出现,定位功能无法正常使用
这样让iOS8正常使用定位功能呢?

<1>你需要在info.plist表里面添加两条变量
在Info.plist中加入两个缺省没有的字段

NSLocationAlwaysUsageDescription//一直允许
NSLocationWhenInUseUsageDescription//使用时允许

iOS 9 网络请求

iOS9把所有的http请求都改为https了:iOS9系统发送的网络请求将统一使用TLS 1.2 SSL。采用TLS 1.2 协议,目的是 强制增强数据访问安全,而且 系统 Foundation 框架下的相关网络请求,将不再默认使用 Http 等不安全的网络协议,而默认采用 TLS 1.2。服务器因此需要更新,以解析相关数据。如不更新,可通过在 Info.plist 中声明,倒退回不安全的网络请求。

  • 1.在Info.plist中添加NSAppTransportSecurity类型Dictionary。
  • 2.在NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES。

使用UIApplicationUserDidTakeScreenshotNotification来检查截图

在iOS7之前,像Snapshot或是Facebook Poke这样的app是使用一些很精巧的方法来检测用户是否有截图。然而,iOS7提供一个崭新的推送方法:UIApplicationUserDidTakeScreenshotNotification。只要像往常一样订阅即可知道什么时候截图了。

调用AVFountain框架实现照相功能

//
//  ViewController.m
//  AVFoundationCamera
//

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);

@interface ViewController ()

@property (strong,nonatomic) AVCaptureSession *captureSession;//负责输入和输出设备之间的数据传递
@property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据
@property (strong,nonatomic) AVCaptureStillImageOutput *captureStillImageOutput;//照片输出流
@property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层
@property (weak, nonatomic) IBOutlet UIView *viewContainer;
@property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按钮
@property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;//自动闪光灯按钮
@property (weak, nonatomic) IBOutlet UIButton *flashOnButton;//打开闪光灯按钮
@property (weak, nonatomic) IBOutlet UIButton *flashOffButton;//关闭闪光灯按钮
@property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光标



@end

@implementation ViewController

#pragma mark - 控制器视图方法
- (void)viewDidLoad {
    [super viewDidLoad];

}

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    //初始化会话
    _captureSession=[[AVCaptureSession alloc]init];
    if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//设置分辨率
        _captureSession.sessionPreset=AVCaptureSessionPreset1280x720;
}
    //获得输入设备
    AVCaptureDevice *captureDevice=[self     getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置摄像头
    if (!captureDevice) {
        NSLog(@"取得后置摄像头时出现问题.");
        return;
    }

    NSError *error=nil;
    //根据输入设备初始化设备输入对象,用于获得输入数据
    _captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
    if (error) {
        NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
        return;
    }
    //初始化设备输出对象,用于获得输出数据
    _captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];
    NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
    [_captureStillImageOutput setOutputSettings:outputSettings];//输出设置

    //将设备输入添加到会话中
    if ([_captureSession canAddInput:_captureDeviceInput]) {
    [_captureSession addInput:_captureDeviceInput];
    }

    //将设备输出添加到会话中
    if ([_captureSession canAddOutput:_captureStillImageOutput]) {
        [_captureSession addOutput:_captureStillImageOutput];
    }

    //创建视频预览层,用于实时展示摄像头状态
    _captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];

    CALayer *layer=self.viewContainer.layer;
    layer.masksToBounds=YES;

    _captureVideoPreviewLayer.frame=layer.bounds;
            _captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//    填充模式
    //将视频预览层添加到界面中
    //[layer addSublayer:_captureVideoPreviewLayer];
    [layer insertSublayer:_captureVideoPreviewLayer     below:self.focusCursor.layer];

    [self addNotificationToCaptureDevice:captureDevice];
    [self addGenstureRecognizer];
    [self setFlashModeButtonStatus];
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    [self.captureSession startRunning];
}

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    [self.captureSession stopRunning];
}

-(void)dealloc{
    [self removeNotification];
}
#pragma mark - UI方法
#pragma mark 拍照
- (IBAction)takeButtonClick:(UIButton *)sender {
    //根据设备输出获得连接
    AVCaptureConnection *captureConnection=[self.captureStillImageOutput     connectionWithMediaType:AVMediaTypeVideo];
    //根据连接取得设备输出的数据
    [self.captureStillImageOutput         captureStillImageAsynchronouslyFromConnection:captureConnection     completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
        if (imageDataSampleBuffer) {
            NSData *imageData=[AVCaptureStillImageOutput     jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage *image=[UIImage imageWithData:imageData];
            UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
//            ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init];
//            [assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage]     orientation:(ALAssetOrientation)[image imageOrientation]     completionBlock:nil];
        }

    }];
}
#pragma mark 切换前后摄像头
- (IBAction)toggleButtonClick:(UIButton *)sender {
    AVCaptureDevice *currentDevice=[self.captureDeviceInput device];
    AVCaptureDevicePosition currentPosition=[currentDevice position];
    [self removeNotificationFromCaptureDevice:currentDevice];
    AVCaptureDevice *toChangeDevice;
    AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;
    if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {
        toChangePosition=AVCaptureDevicePositionBack;
    }
    toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];
    [self addNotificationToCaptureDevice:toChangeDevice];
    //获得要调整的设备输入对象
    AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];

    //改变会话的配置前一定要先开启配置,配置完成后提交配置改变
    [self.captureSession beginConfiguration];
    //移除原有输入对象
    [self.captureSession removeInput:self.captureDeviceInput];
    //添加新的输入对象
    if ([self.captureSession canAddInput:toChangeDeviceInput]) {
        [self.captureSession addInput:toChangeDeviceInput];
        self.captureDeviceInput=toChangeDeviceInput;
    }
    //提交会话配置
    [self.captureSession commitConfiguration];

    [self setFlashModeButtonStatus];
}

#pragma mark 自动闪光灯开启
- (IBAction)flashAutoClick:(UIButton *)sender {
    [self setFlashMode:AVCaptureFlashModeAuto];
    [self setFlashModeButtonStatus];
}
#pragma mark 打开闪光灯
- (IBAction)flashOnClick:(UIButton *)sender {
    [self setFlashMode:AVCaptureFlashModeOn];
[self setFlashModeButtonStatus];
}
#pragma mark 关闭闪光灯
- (IBAction)flashOffClick:(UIButton *)sender {
    [self setFlashMode:AVCaptureFlashModeOff];
    [self setFlashModeButtonStatus];
}

#pragma mark - 通知
/**
 *  给输入设备添加通知
 */
-(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{
    //注意添加区域改变捕获通知必须首先设置设备允许捕获
    [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
        captureDevice.subjectAreaChangeMonitoringEnabled=YES;
    }];
    NSNotificationCenter *notificationCenter= [NSNotificationCenter     defaultCenter];
    //捕获区域发生改变
    [notificationCenter addObserver:self selector:@selector(areaChange:)     name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}
-(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{
    NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
    [notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}
/**
 *  移除所有通知
 */
-(void)removeNotification{
    NSNotificationCenter *notificationCenter= [NSNotificationCenter     defaultCenter];
    [notificationCenter removeObserver:self];
}

-(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{
    NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
    //会话出错
    [notificationCenter addObserver:self     selector:@selector(sessionRuntimeError:)     name:AVCaptureSessionRuntimeErrorNotification object:captureSession];
}

/**
 *  设备连接成功
 *
 *  @param notification 通知对象
 */
-(void)deviceConnected:(NSNotification *)notification{
    NSLog(@"设备已连接...");
}
/**
 *  设备连接断开
 *
 *  @param notification 通知对象
 */
-(void)deviceDisconnected:(NSNotification *)notification{
NSLog(@"设备已断开.");
}
/**
 *  捕获区域改变
 *
 *  @param notification 通知对象
 */
-(void)areaChange:(NSNotification *)notification{
    NSLog(@"捕获区域改变...");
}

/**
 *  会话出错
 *
 *  @param notification 通知对象
 */
-(void)sessionRuntimeError:(NSNotification *)notification{
    NSLog(@"会话发生错误.");
}

#pragma mark - 私有方法

/**
 *  取得指定位置的摄像头
 *
 *  @param position 摄像头位置
 *
 *  @return 摄像头设备
 */
-(AVCaptureDevice *)getCameraDeviceWithPosition:    (AVCaptureDevicePosition )position{
NSArray *cameras= [AVCaptureDevice     devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *camera in cameras) {
        if ([camera position]==position) {
            return camera;
        }
    }
    return nil;
}

/**
 *  改变设备属性的统一操作方法
 *
 *  @param propertyChange 属性改变操作
 */
-(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
    AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
    NSError *error;
    //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用    unlockForConfiguration方法解锁
    if ([captureDevice lockForConfiguration:&error]) {
        propertyChange(captureDevice);
        [captureDevice unlockForConfiguration];
    }else{
        NSLog(@"设置设备属性过程发生错误,错误信息:    %@",error.localizedDescription);
    }
}

/**
 *  设置闪光灯模式
 *
 *  @param flashMode 闪光灯模式
 */
-(void)setFlashMode:(AVCaptureFlashMode )flashMode{
    [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isFlashModeSupported:flashMode]) {
        [captureDevice setFlashMode:flashMode];
    }
}];
}
/**
 *  设置聚焦模式
 *
 *  @param focusMode 聚焦模式
 */
-(void)setFocusMode:(AVCaptureFocusMode )focusMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isFocusModeSupported:focusMode]) {
        [captureDevice setFocusMode:focusMode];
    }
}];
}
/**
 *  设置曝光模式
 *
 *  @param exposureMode 曝光模式
 */
-(void)setExposureMode:(AVCaptureExposureMode)exposureMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isExposureModeSupported:exposureMode]) {
        [captureDevice setExposureMode:exposureMode];
    }
}];
}
/**
 *  设置聚焦点
 *
 *  @param point 聚焦点
 */
-(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
    if ([captureDevice isFocusModeSupported:focusMode]) {
        [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
    }
    if ([captureDevice isFocusPointOfInterestSupported]) {
        [captureDevice setFocusPointOfInterest:point];
    }
    if ([captureDevice isExposureModeSupported:exposureMode]) {
        [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
    }
    if ([captureDevice isExposurePointOfInterestSupported]) {
        [captureDevice setExposurePointOfInterest:point];
    }
}];
}

/**
 *  添加点按手势,点按时聚焦
 */
-(void)addGenstureRecognizer{
UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];
[self.viewContainer addGestureRecognizer:tapGesture];
}
-(void)tapScreen:(UITapGestureRecognizer *)tapGesture{
CGPoint point= [tapGesture locationInView:self.viewContainer];
//将UI坐标转化为摄像头坐标
CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
[self setFocusCursorWithPoint:point];
[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
}

/**
 *  设置闪光灯按钮状态
 */
-(void)setFlashModeButtonStatus{
AVCaptureDevice *captureDevice=[self.captureDeviceInput device];
AVCaptureFlashMode flashMode=captureDevice.flashMode;
if([captureDevice isFlashAvailable]){
    self.flashAutoButton.hidden=NO;
    self.flashOnButton.hidden=NO;
    self.flashOffButton.hidden=NO;
    self.flashAutoButton.enabled=YES;
    self.flashOnButton.enabled=YES;
    self.flashOffButton.enabled=YES;
    switch (flashMode) {
        case AVCaptureFlashModeAuto:
            self.flashAutoButton.enabled=NO;
            break;
        case AVCaptureFlashModeOn:
            self.flashOnButton.enabled=NO;
            break;
        case AVCaptureFlashModeOff:
            self.flashOffButton.enabled=NO;
            break;
        default:
            break;
    }
}else{
    self.flashAutoButton.hidden=YES;
    self.flashOnButton.hidden=YES;
    self.flashOffButton.hidden=YES;
}
}

/**
 *  设置聚焦光标位置
 *
 *  @param point 光标位置
 */
-(void)setFocusCursorWithPoint:(CGPoint)point{
self.focusCursor.center=point;
self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
self.focusCursor.alpha=1.0;
[UIView animateWithDuration:1.0 animations:^{
    self.focusCursor.transform=CGAffineTransformIdentity;
} completion:^(BOOL finished) {
    self.focusCursor.alpha=0;

}];
}