关于JSONModel用法的一些简单介绍

早先我们设计一个Model的时候其实就是将一个字典NSDictionary中的Value有针对性的一个一个取出然后赋值;
eg:

self.id = [jsonDict objectForKey:@“id”];
self.name = [jsonDict objectForKey:@“name”];
self.profileImageBig = [jsonDict objectForKey:@“profile_image_big”];
self.profileImageSmall = [jsonDict objectForKey:@“profile_image_small”];
self.profileImageSquare = [jsonDict objectForKey:@“profile_image_square”];
self.firstName = [jsonDict objectForKey:@“firstName”];
self.familyName = [jsonDict objectForKey:@“familyName”];
self.age = [jsonDict objectForKey:@“age”];

当然我们有更好的方法,例如有很多第三方的解析库,这里我们使用JSONModel来进行JSON的实例化.

Modle属性命名的注意地方

  • 我们只需要设置对应的Property熟悉即可,这个属性的命名 可以严格按照NSDictionary中的key名对应。
    eg:
{
  “id” : 1,
  “name”: Jon,
}

@interface MyModel: JSONModel
@property (strong, nonatomic) NSString* id;
@property (strong, nonatomic) NSString* name;

@end
  • 也可以自己自由命名Property 属性名,不过要在实现方法里实现方法:+ (JSONKeyMapper*)keyMapper
{
  “id” : 1,
  “name”: Jon,
}

@interface MyModel: JSONModel
@property (strong, nonatomic) NSString* uid;
@property (strong, nonatomic) NSString* username;
@end

#import “MyModel.h”
@implementation MyModel
+ (JSONKeyMapper*)keyMapper {
    NSDictionary *map = @{ @“id”: @“uid”, @“name”: @“username” };
    return [[JSONKeyMapper alloc] initWithDictionary:map];
}
@end

属性命名类型

在我们不实用JSONModel,自己定义一个模型的时候当你要声明类型的时候还需要在实现方法里进行类型转换,比如

@property (nonatomic, assign) NSInteger id;;

self.id = [jsonDict objectForKey:@“id”] IntegerValue];

使用JSONModel的时候可以不必关心实现方法,你只管声明你需要的数据类型即可:

{
  “purchaseDate” : “2012-11-26T10:00:01+02:00”,
  “blogURL” : “http://www.touch-code-magazine.com”
}

@interface SmartModel: JSONModel
 
@property (strong, nonatomic) NSDate* purchaseDate;
@property (strong, nonatomic) NSURL* blogUrl;
 
@end

ps:当然如果你强制讲一个本是NSString的文笔声明为一个int 这种问题我们不做讨论。

多层封装和对象

  • 对于一个NSDictionary如果一个value也是一个NSDictionary,那么如果你或许会考虑将这个子NSDictionary声明为一个对象类.
{
  “idImage”: 1, 
  “name”: “house.jpg”, 
  “copyright”: {“author”:”Marin Todorov”, “year”:2012} 
}

@interface CopyModel: JSONModel
 
@property (strong, nonatomic) NSString* author;
@property (assign, nonatomic) int year;
 
@end


#import “CopyModel.h”
@interface ImageModel: JSONModel
 
@property (assign, nonatomic) int idImage;
@property (strong, nonatomic) NSString* name;
@property (strong, nonatomic) CopyModel* copyright;
 
@end

上面的例子就是这样的一个情况,当然每个类的实现方法都按照自己的声明进行自由实现。

  • 那么如果这个value是一个包含字典的数组,例如:
{
  “idImage”: 1, 
  “name”: “house.jpg”, 
  “copyrights”: [{“author”:”Marin Todorov”, “year”:2012},{“author”:”Marin Todorov”, “year”:2012},{“author”:”Marin Todorov”, “year”:2012},...] 
}

@protocol CopyModel @end
@interface CopyModel: JSONModel
@property (strong, nonatomic) NSString* author;
@property (assign, nonatomic) int year;
 
@end

#import “CopyModel.h”
@interface ImageModel: JSONModel
 
@property (assign, nonatomic) int idImage;
@property (strong, nonatomic) NSString* name;
@property (strong, nonatomic) NSArray<CopyModel>* copyrights;
 
@end

上面的情况 要注意的地方是

@protocol CopyModel @end

这个协议是为了匹配这个对象命名的如果没有则会提示报错。

将下一层属性提到上一层解析

eg

{
  “order_id”: 104,
  “order_details” : [
    {
      “name”: “Product#1”,
      “price”: {
        “usd”: 12.95
      }
    }
  ]
}

对于上面的这样一个JSON 你想把价格usd提到上一个对象
可以使用 .联系到一起

@interface OrderModel : JSONModel
@property (assign, nonatomic) int id;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSString* productName;
@end

@implementation OrderModel

+(JSONKeyMapper*)keyMapper
{
  return [[JSONKeyMapper alloc] initWithDictionary:@{
    @“order_id”: @“id”,
    @“order_details.name”: @“productName”,
    @“order_details.price.usd”: @“price”
  }];
}

@end

可选属性

在这样的一个场景,当API返回的接口中某个字段不是必选返回的时候,既有值的时候返回,没有的时候这个key就没有,还有一种情况是返回的值是一个空对象, 这种情况下,如果我们上上面的写法声明属性会发现JSONModel无法解析,这时候就需要用到 Optional
eg:

//this property is required
@property (strong, nonatomic) NSString* string;
 
//this one’s optional
@property (strong, nonatomic) NSNumber<Optional>* number;

忽略属性

对于一些属性可以忽略解析的,可以使用*Ignore_进行忽略

{
  “id”: “123”,
  “name”: null
}

@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString<Ignore>* customProperty;
@end

@implementation ProductModel
@end

当然也可以在实现方法里复写类方法

@implementation ProductModel
+(BOOL)propertyIsOptional:(NSString*)propertyName
{
     if ([propertyName isEqualToString:@“name”]) {
        return YES;
    }
  return NO;
}
@end

参考:JSONModel简介 ,Magical Data Modelling Framework for JSON

NSURLRequestCachePolicy—iOS缓存策略

NSURLRequestCachePolicy指定缓存逻辑。URL加载系统提供了一个磁盘和内存混合的缓存,来相应网络请求。这个缓存允许一个应用减少对网络连接的依赖,并且增加性能。使用缓存的目的是为了使用的应用程序能更快速的响应用户输入,是程序高效的运行。有时候我们需要将远程web服务器获取的数据缓存起来,减少对同一个url多次请求。
NSURLRequestUseProtocolCachePolicy = 0, 默认缓存策略。具体工作:如果一个NSCachedURLResponse对于请求并不存在,数据将会从源端获取。如果请求拥有一个缓存的响应,那么URL加载系统会检查这个响应来决定,如果它指定内容必须重新生效的话。假如内容必须重新生效,将建立一个连向源端的连接来查看内容是否发生变化。假如内容没有变化,那么响应就从本地缓存返回数据。如果内容变化了,那么数据将从源端获取
NSURLRequestReloadIgnoringLocalCacheData = 1, URL应该加载源端数据,不使用本地缓存数据
NSURLRequestReloadIgnoringLocalAndRemoteCacheData =4, 本地缓存数据、代理和其他中介都要忽视他们的缓存,直接加载源数据
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData, 两个的设置相同
NSURLRequestReturnCacheDataElseLoad = 2, 指定已存的缓存数据应该用来响应请求,不管它的生命时长和过期时间。如果在缓存中没有已存数据来响应请求的话,数据从源端加载。
NSURLRequestReturnCacheDataDontLoad = 3, 指定已存的缓存数据用来满足请求,不管生命时长和过期时间。如果在缓存中没有已存数据来响应URL加载请求的话,不去尝试从源段加载数据,此时认为加载请求失败。这个常量指定了一个类似于离线模式的行为
NSURLRequestReloadRevalidatingCacheData = 5 指定如果已存的缓存数据被提供它的源段确认为有效则允许使用缓存数据响应请求,否则从源段加载数据。
只有响应http和https的请求会被缓存。ftp和文件协议当被缓存策略允许的时候尝试接入源段。自定义的NSURLProtocol类能够保护缓存,如果它们被选择使用的话。

小结:NSURLRequestReturnCacheDataDontLoad是用于离线模式的,我为了能让用户在离线下面阅读,我就设计了当没有网络的时候的策略为NSURLRequestReturnCacheDataDontLoad。
代码如下:

if (有网) {
cachePolicy = NSURLRequestUseProtocolCachePolicy;
}
else{
cachePolicy = NSURLRequestReturnCacheDataDontLoad;
}

Markdown syntax guide and writing on MWeb

Philosophy

Markdown is intended to be as easy-to-read and easy-to-write as is feasible.
Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it's been marked up with tags or formatting instructions.
Markdown's syntax is intended for one purpose: to be used as a format for writing for the web.

继续阅读

MWeb – 专业的Markdown写作、记笔记、静态博客生成软件

专业的Markdown写作支持

  • 极简UI、漂亮的Markdown语法高亮、列表缩进优化,提供7种主题选择。除了支持基本的Markdown语法外,还支持以下Markdown扩展语法:
    • 支持Table
    • 支持TOC(Table Of Content)
    • 支持MathJax
    • 支持代码高亮(Fenced code block)
    • 支持任务列表(Task lists)列表后再加[ ][x]
    • 支持顺序图和流程图
    • 支持Strikethrough、Underline、Superscript、Autolink、Footnote等。
  • 支持即时预览并提供6种预览主题,其中二种和静态博客主题相对应,也就是说您在写博客时可以即时预览大概效果!所有主题效果都支持导出为HTML、PDF。快捷键 CMD + R 打开即时预览窗口。
  • 支持大纲视图,长文档时跳转非常方便。

设计为两种模式

  • 外部文档模式:用于新建、打开和编辑外部Markdown文档,就像系统的文本编辑器。
  • 文档库模式:用分类树管理文档,可以把文档设为多个分类,用于记笔记和静态网站生成。此模式中的文档可以用拖放或粘贴插入图片并直接显示。CMD + V 粘贴为JPG格式,CMD + Shift + V 粘贴为PNG透明格式。

外部文档模式使用 CMD + N 新建、CMD + O 打开。
文档库模式使用 CMD + Shift + N 或搜寻右边的新建文档按钮新建、CMD + L打开文档库。

文档库模式用于记笔记

文档库模式使用分类树组织和管理文档,支持拖放或粘贴插入图片并直接显示,插入非图片则会生成连结。
支持把Markdown或文本文档导入到文档库,也支持把整个分类或者文档(可选多个)导出为HTML、PDF、Markdown。

更详细的信息请看:MWeb 文档库模式详细说明

文档库模式用于静态博客生成

一键把分类生成静态博客,目前可选二个主题,支持自定主题。只要填入Disqus、多说提供的代码即可以为博客增加评论功能。可勾选让网站支持MathJax和顺序图、流程图。

更详细的信息请看:MWeb 生成静态博客详细说明

GCD介绍: 基本概念和Dispatch Queue

iOS的三种多线程技术

1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程)
2.以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题
ØNSOperation/NSOperationQueue 面向对象的线程技术
ØGCD —— Grand Central Dispatch(派发) 是基于C语言的框架,可以充分利用多核,是苹果推荐使用的多线程技术

以上这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的,在项目中很多框架技术分别使用了不同多线程技术。

2.三种多线程技术的对比

•NSThread:
–优点:NSThread 比其他两个轻量级,使用简单
–缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销
•NSOperation:
–不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上
–NSOperation是面向对象的
•GCD:
–Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术
–GCD是基于C语言的

什么是GCD?

Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。GCD比之NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分。

除了代码的平行执行能力,GCD还提供高度集成的事件控制系统。可以设置句柄来响应文件描述符、mach ports(Mach port 用于 OS X上的进程间通讯)、进程、计时器、信号、用户生成事件。这些句柄通过GCD来并发执行。

GCD的API很大程度上基于block,当然,GCD也可以脱离block来使用,比如使用传统c机制提供函数指针和上下文指针。实践证明,当配合block使用时,GCD非常简单易用且能发挥其最大能力。

你可以在Mac上敲命令“man dispatch”来获取GCD的文档。

为何使用?

GCD提供很多超越传统多线程编程的优势:

  1. 易用: GCD比之thread跟简单易用。由于GCD基于work unit而非像thread那样基于运算,所以GCD可以控制诸如等待任务结束监视文件描述符周期执行代码以及工作挂起等任务。基于block的血统导致它能极为简单得在不同代码作用域之间传递上下文。
  2. 效率: GCD被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。这关系到易用性:导致GCD易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了。
  3. 性能: GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。

Dispatch Objects

尽管GCD是纯c语言的,但它被组建成面向对象的风格。GCD对象被称为dispatch object。Dispatch object像Cocoa对象一样是引用计数的。使用dispatch_release和dispatch_retain函数来操作dispatch object的引用计数来进行内存管理。但主意不像Cocoa对象,dispatch object并不参与垃圾回收系统,所以即使开启了GC,你也必须手动管理GCD对象的内存。

Dispatch queues 和 dispatch sources(后面会介绍到)可以被挂起和恢复,可以有一个相关联的任意上下文指针,可以有一个相关联的任务完成触发函数。可以查阅“man dispatch_object”来获取这些功能的更多信息。

Dispatch Queues

GCD的基本概念就是dispatch queue。dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatch queue可以是并发的或串行的。并发任务会像NSOperationQueue那样基于系统负载来合适地并发进行,串行队列同一时间只执行单一任务。

GCD中有三种队列类型:

  1. The main queue: 与主线程功能相同。实际上,提交至main queue的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。
  2. Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。
  3. 用户队列: 用户队列 (GCD并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数 dispatch_queue_create 创建的队列. 这些队列是串行的。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mutex。
Dispatch Queues的生成可以有这几种方式:

1. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.serial", DISPATCH_QUEUE_SERIAL); //生成一个串行队列,队列中的block按照先进先出(FIFO)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称,在调试程序时会非常有用,所有尽量不要重名了。

2. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.concurrent", DISPATCH_QUEUE_CONCURRENT); //生成一个并发执行队列,block被分发到多个线程去执行

3. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //获得程序进程缺省产生的并发队列,可设定优先级来选择高、中、低三个优先级队列。由于是系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。需要注意的是,三个队列不代表三个线程,可能会有更多的线程。并发队列可以根据实际情况来自动产生合理的线程数,也可理解为dispatch队列实现了一个线程池的管理,对于程序逻辑是透明的。

官网文档解释说共有三个并发队列,但实际还有一个更低优先级的队列,设置优先级为DISPATCH_QUEUE_PRIORITY_BACKGROUND。Xcode调试时可以观察到正在使用的各个dispatch队列。

4. dispatch_queue_t queue = dispatch_get_main_queue(); //获得主线程的dispatch队列,实际是一个串行队列。同样无法控制主线程dispatch队列的执行继续或中断。

接下来我们可以使用dispatch_async或dispatch_sync函数来加载需要运行的block。
dispatch_async(queue, ^{

//block具体代码

}); //异步执行block,函数立即返回

dispatch_sync(queue, ^{

//block具体代码

}); //同步执行block,函数不返回,一直等到block执行完毕。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。</pre>
实际编程经验告诉我们,尽可能避免使用dispatch_sync,嵌套使用时还容易引起程序死锁。

如果queue1是一个串行队列的话,这段代码立即产生死锁:

dispatch_sync(queue1, ^{

dispatch_sync(queue1, ^{

......

});

......

});

那实际运用中,一般可以用dispatch这样来写,常见的网络请求数据多线程执行模型:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

//子线程中开始网络请求数据

//更新数据模型

dispatch_sync(dispatch_get_main_queue(), ^{

//在主线程中更新UI代码

});

});

程序的后台运行和UI更新代码紧凑,代码逻辑一目了然。

dispatch队列是线程安全的,可以利用串行队列实现锁的功能。比如多线程写同一数据库,需要保持写入的顺序和每次写入的完整性,简单地利用串行队列即可实现:

dispatch_queue_t queue1 = dispatch_queue_create("com.dispatch.writedb", DISPATCH_QUEUE_SERIAL);

- (void)writeDB:(NSData *)data

{

dispatch_async(queue1, ^{

//write database

});

}

下一次调用writeDB:必须等到上次调用完成后才能进行,保证writeDB:方法是线程安全的。

dispatch队列还实现其它一些常用函数,包括:

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); //重复执行block,需要注意的是这个方法是同步返回,也就是说等到所有block执行完毕才返回,如需异步返回则嵌套在dispatch_async中来使用。多个block的运行是否并发或串行执行也依赖queue的是否并发或串行。

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); //这个函数可以设置同步执行的block,它会等到在它加入队列之前的block执行完毕后,才开始执行。在它之后加入队列的block,则等到这个block执行完毕后才开始执行。

void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); //同上,除了它是同步返回函数

void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); //延迟执行block

最后再来看看dispatch队列的一个很有特色的函数:

void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);

它会把需要执行的任务对象指定到不同的队列中去处理,这个任务对象可以是dispatch队列,也可以是dispatch源(以后博文会介绍)。而且这个过程可以是动态的,可以实现队列的动态调度管理等等。比如说有两个队列dispatchA和dispatchB,这时把dispatchA指派到dispatchB:

dispatch_set_target_queue(dispatchA, dispatchB);

那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行:

dispatch_suspend(dispatchA);

则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。

eg:参考:
http://www.dreamingwish.com/dream-2012/gcd%E4%BB%8B%E7%BB%8D%EF%BC%88%E4%B8%80%EF%BC%89-%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8Cdispatch-queue.html
http://www.cnblogs.com/sunfrog/p/3305614.html

[转]iOS7: 那些容易被忽视的新特性

iOS7到现在已经发布了有一段时间了。相信你现在已经了解了它那些开创性的视觉设计,已经了解了它的新的API,比如说SpirteKit,UIKit Dynamics以及TextKit,作为开发者,也很可能已经在使用Xcode5进行开发了。

     然而,它新颖以及备受争议的特性,让iOS7成为iOS系统史上最大的发布之一。除非你是那种用整晚的时间阅读iOS7更新内容的那种人,那么就可能会忽视掉一两个新的变化。
     在本篇文章中我们汇总了一份较完备的iOS7重要且有趣的新变化。现在让我们一起来看看是否有一些新变化你以前没有注意到呢?
坏消息,好消息,以及非常好的消息
    iOS7有一些坏消息,一些好消息,还有一些非常好的消息。
坏消息:iOS7中有一些可能会对app造成破坏性的变化,你必须要了解。如果你还不知道这些变化的话,那么你   就需要好好看看他们了,因为当他们在iOS7上运行的时候可能会造成app崩溃!
好消息:有一些你熟悉的特性和API在iOS7中得到了优化——但是还有一些其他的特性被遗弃了。花点时间来看看这些变化,对你app的升级来说是个很好的投资。
 
非常好的消息:iOS7的发布确实震动了手机开发世界,随着这个重大事件的发生,随之而来的也有一系列新功能,它们可能会给你现有app带来新特色,也可能成为将来开发的app创新的触发剂。
 
本篇文章搜罗了iOS7容易忽略的一些特点,将它们分为了以上三类。以下的列表,如果有感兴趣的内容可以直接跳过去看,也可以按照文章的顺序来了解所有的变化。
 
 坏消息:可以导致app崩溃的变化
1.已禁用-[UIDevice uniqueIdentifier]
2.UIPasteboard由共享变为沙盒化了
3.MAC地址不能再用来识别设备
4.iOS现在要求app如需使用麦克风,需要征得用户同意
 
好消息:性能提高以及被遗弃的功能
5.-[NSArray firstObject]的实现
6.增加了instancetype
7.设置UIImage的渲染模式:UIImage.renderingMode
8.tintColor VS barTintColor
9.去掉了纹理颜色
10.UIButtonTypeRoundRect被UIButtonTypeSystem取代了
 
非常好的消息:新功能
11.检查无线路由是否可用
12.了解蜂窝网络
13.通过iCloud同步用户设备的密码
14.使用NSAttributedString显示HTML
15.使用原生的Base64
16.使用UIApplicationUserDidTakeScreenshotNotification来检查截图
17.实现多语言语音合成
18.使用了新的手势识别
19.使用UIScrollViewKeyboardDismissMode实现了Message app的行为
20.使用Core Image来检测眨眼以及微笑
21.给UITextView增加了链接
 
坏消息:可以导致app崩溃的变化
这个部分的变化你可能在了解iOS7的时候已经注意到了,但是你也许没有意识到这些变化的程度,以及它们如何可能会影响你的app。事实上这些变化都和用户隐私相关,而你应该知道对苹果来说用户隐私有多么重要!
 
1.已禁用-[UIDevice uniqueIdentifier]
苹果总是把用户的隐私看的很重要。-[UIDevice uniqueIdentifier]在iOS5实际在iOS5的时候已经被遗弃了,但是iOS7中已经完全的禁用了它。Xcode5甚至不会允许你编译包含了指引到-[UIDevice uniqueIdentifier]的app。此外,iOS7之前的使用了-[UIDevice uniqueIdentifier] 的app如果在iOS7上运行,它不会返回设备的UUID,而是会返回一串字符串,以FFFFFFFF开头,跟着-[UIDevice identifierForVendor]的十六进制值。
 
2.UIPasteboard由共享变为沙盒化了
 UIPasteboard过去是用来做app之间的数据分享的。UIPasteboard本无问题,但是开发者开始使用它来存储标识符,和其他的相关app分享这些标识符的时候问题就出现了。有一个使用这种把戏的就是OpenUDID。
 
在iOS7中,使用 +[UIPasteboard pasteboardWithName:create:]和 +[UIPasteboard pasteboardWithUniqueName]创建剪贴板,而且只对相同的app group可见,这样就让OpenUDID不那么有用了。
 
3.MAC地址不能再用来设别设备
  iOS7: <wbr>那些容易被忽视的新特性
 现在仍可以使用这个MAC
 
还有一个生成iOS设备唯一标示符的方法是使用iOS设备的Media Access Control(MAC)地址。一个MAC地址是一个唯一的号码,它是物理网络层级方面分配给网络适配器的。这个地址苹果还有其他的名字,比如说是硬件地址(Hardware Address)或是Wifi地址,都是指同样的东西。
 
有很多工程和框架都使用这个方法来生成唯一的设备ID。比如说ODIN。然而,苹果并不希望有人通过MAC地址来分辨用户,所以如果你在iOS7系统上查询MAC地址,它现在只会返回02:00:00:00:00:00。
 
现在苹果明确的表明你应该使用-[UIDevice identifierForVendor]或是-[ASIdentifierManager advertisingIdentifier]来作为你框架和应用的唯一标示符。坦白的来说,应对这些变化也不是那么的难,见以下代码片段:
NSString *identifierForVendor = [[UIDevice currentDevice].identifierForVendor UUIDString]; 
NSString *identifierForAdvertising = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString]; 
每种方法都适配一种特别的用法:
identifierForVendor对供应商来说是唯一的一个值,也就是说,由同一个公司发行的的app在相同的设备上运行的时候都会有这个相同的标识符。然而,如果用户删除了这个供应商的app然后再重新安装的话,这个标识符就会不一致。
 
advertisingIdentifier会返回给在这个设备上所有软件供应商相同的 一个值,所以只能在广告的时候使用。这个值会因为很多情况而有所变化,比如说用户初始化设备的时候便会改变。
 
如果你想了解更多的信息,你可以看看这篇文章
 
4.iOS现在要求app如需使用麦克风,需要征得用户同意
以前如果app需要使用用户的位置,通讯录,日历,提醒以及照片,接受推送消息,使用用户的社交网络的时候需要征得用户的同意。现在在iOS7当中,使用麦克风也需要取得用户同意了。如果用户不允许app使用麦克风的话,那么需要使用麦克风的app就不能接收不到任何声音。
 
以下的代码是用来查询用户是否允许app使用麦克风:
//第一次调用这个方法的时候,系统会提示用户让他同意你的app获取麦克风的数据 
// 其他时候调用方法的时候,则不会提醒用户 
// 而会传递之前的值来要求用户同意 
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) { 
    if (granted) { 
        // 用户同意获取数据 
    } else { 
        // 可以显示一个提示框告诉用户这个app没有得到允许? 
    } 
}]; 
你同时还要注意,如果你在获得用户的同意之前使用任何方法来使用麦克风的话,会引起iOS系统弹出以下警示栏:

iOS7: <wbr>那些容易被忽视的新特性

 
好消息:性能提高以及被遗弃的功能
以上就是一些重要的更新,他们可能会让你现在的app崩溃。然后,还有一些变化可能会对你的app造成影响,但是你不会在第一时间发现这些变化。
 
5.-[NSArray firstObject]的实现
-[NSArray firstObject]可能是Objective-C中被调用做多的API。在Open Radar上一个简单的调查显示有一些需求苹果已经做了记录。好消息是现在这些需求已经得到了解决。. firstObject的使用可以追溯到iOS4.0,但是那时仅仅是一个私有方法。在iOS7以前,工程师用下面的方式来使用它:
NSArray *arr = @[]; 
id item = [arr firstObject]; 
// 之前你需要做以下工作 
id item = [arr count] > 0 ? arr[0] : nil; 
因为上面的方式很平常,有些人将它作为一个类增加到NSArray中,然后创建他们自己的firstObject方法。在Github上做一个快速搜索你可以看到过去这种方式是有多么的常用。
 
 这个方法的问题是这个方法的名字必须是唯一的,否则的话这个方法所引发的问题无法预估。请确保检查你是否有任何自定义的代码在NSArray上实现了firstObject,如果有的话看看它是否是必须的,不是必须的话就把它全部移除。
 
6.增加了instancetype
instancetype让iOS7API变得更加难懂。苹果改变了大部分 initializer和简易构造函数(convenience constructors),用instancetype代替id作返回类型。但是这个instancetype是什么呢?
 
instancetype用来在声明一个方法时告诉编译器其返回类型,它表示返回调用该方法的类的对象。这比之前返回id的通常做法要好,编译器可以对返回类型做一些检查,如果出现错误,在编译时就能提醒你,而不是在程序运行时发生崩溃。同时,在调用子类方法时,使用它还可以省去对返回值的强制类型转换,编译器能够正确推断方法的返回值类型。
 
要说到instancetaype的缺点和优点吗?基本上,在任何可能的情况下都可以使用它。
 
如果需要更多关于instancetype的信息,你可以看看这篇文章
 
 7.设置UIImage的渲染模式:UIImage.renderingMode
着色(Tint Color)是iOS7界面中的一个重大改变,你可以设置一个UIImage在渲染时是否使用当前视图的Tint Color。UIImage新增了一个只读属性:renderingMode,对应的还有一个新增方法:imageWithRenderingMode:,它使用UIImageRenderingMode枚举值来设置图片的renderingMode属性。该枚举中包含下列值:
UIImageRenderingModeAutomatic // 根据图片的使用环境和所处的绘图上下文自动调整渲染模式。 
UIImageRenderingModeAlwaysOriginal // 始终绘制图片原始状态,不使用Tint Color。 
UIImageRenderingModeAlwaysTemplate // 始终根据Tint Color绘制图片,忽略图片的颜色信息。 
renderingMode属性的默认值是UIImageRenderingModeAutomatic,即UIImage是否使用Tint Color取决于它显示的位置。其他情况可以看下面的图例

iOS7: <wbr>那些容易被忽视的新特性

 
以下的代码说明了使用一个既定的rendering模式创建图片是多么简单:
UIImage *img = [UIImage imageNamed:@"myimage"]; 
img = [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 
8.tintcolor VS barTintColor
iOS7中你可以使用一个给定的颜色,甚至是记入颜色主题来给整个app着色,帮助你的app脱颖而出。设置app的tint color很简答,只要使用UIView的新属性tintColor即可。
 
这个属性是否听上去很熟悉呢?应该很熟悉,有些类,比如说UINaviagtionBar,UISearchBar,UITabBar以及UIToolbar已经有了这么命名的属性。他们现在有了一个新的属性:barTintColor。
 
为了避免使用新属性的时候犯错误,如果你的appp需要支持iOS6以前的系统的时候,请检查一下。
UINavigationBar *bar = self.navigationController.navigationBar; 
UIColor *color = [UIColor greenColor]; 
if ([bar respondsToSelector:@selector(setBarTintColor:)]) { // iOS 7+ 
    bar.barTintColor = color; 
} else { // what year is this? 2012? 
    bar.tintColor = color; 
} 

iOS7: <wbr>那些容易被忽视的新特性

 
纹理颜色?对,不再使用他们了。不能再创建可以展现纹理的颜色。根据UIInterface.h文件中的注释,-[UIColor groupTableViewBackgroundColor]应该是要在iOS6当中即被删除了,但是它仅仅只是不像之前那样返回纹理颜色了。然而,以下的颜色在iOS7当中被删除了:
+ (UIColor *)viewFlipsideBackgroundColor; 
+ (UIColor *)scrollViewTexturedBackgroundColor; 
+ (UIColor *)underPageBackgroundColor; 
在iOS开发刚开始就陪伴着你的老朋友现在也被删除了,它就是UIButtonTypeRoundRect ,被新的UIButtonTypeSystem取代了。
 
非常好的消息:新功能
如果每次iOS系统的发布都没有一些新的功能会是什么样子?这些新功能相信大部分开发者已经知道了,你可能会发现一些新颖的方式将它们整合到你的app中去!
iOS7: <wbr>那些容易被忽视的新特性
 
11.检查无线路由是否可用
定制一个视频播放器的能力在iOS版本每次的发布中一直有所进步。比如说,在iOS6之前,你不能在MPVolumeView中改变AirPlay的icon。
 
在iOS7当中,你可以通过AirPlay,蓝牙或是其他的虚线机制了解是否有一个远程的设备可用。了解它的话,就可以让你的app在恰当的时候做恰当的事,比如说,在没有远程设备的时候就不显示AirPlay的icon。
 
以下是新增加到MPVolumeView的新属性和推送
@property (nonatomic, readonly) BOOL wirelessRoutesAvailable; //  是否有设备可以连接的无线线路? 
@property (nonatomic, readonly) BOOL wirelessRouteActive; // 设备现在是否连接上了网络 
NSString *const MPVolumeViewWirelessRoutesAvailableDidChangeNotification; 
NSString *const MPVolumeViewWirelessRouteActiveDidChangeNotification;
 
12.了解蜂窝网络
在iOS7之前,是使用Reachability来检测设备是否连接到WWAN或是Wifi的。iOS7在这个基础上更进了一步,它会告诉你的设备连接上的是那种蜂窝网络,比如说是Edge网络,HSDPA网络,或是LTE网络。告诉用户他们连接上的是哪种网络可以优化用户体验,因为这样他们会知道网速如何,不会去请求需要高网速的网络请求。
 
这是CTTelephonyNetworkInfo的部分功能,它是CoreTelephony框架的一部分。iOS7还增加了currentRadioAccessTechnology属性和CTRadioAccessTechnologyDidChangeNotification到这个类。还有一些新的字符串常量来定义可能的值,比如说是CTRadioAccessTechnologyLTE。
 
以下代码告诉你在app delegate中如何使用这个新功能:
@import CoreTelephony.CTTelephonyNetworkInfo; // new modules syntax! 
 @interface AppDelegate ()  
// we need to keep a reference to the CTTelephonyNetworkInfo object, otherwise the notifications won't be fired! 
@property (nonatomic, strong) CTTelephonyNetworkInfo *networkInfo; 
  @end 
  
@implementation ViewController  
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 
  // whatever stuff your method does... 
  
  self.networkInfo = [[CTTelephonyNetworkInfo alloc] init]; 
  NSLog(@"Initial cell connection: %@", self.networkInfo.currentRadioAccessTechnology); 
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(radioAccessChanged) name:
CTRadioAccessTechnologyDidChangeNotification object:nil]; 
  
  // whatever stuff your method does... 
} 
  
- (void)radioAccessChanged { 
  NSLog(@"Now you're connected via %@", self.networkInfo.currentRadioAccessTechnology); 
} 
  
@end 
注意:研究一下CTTelephonyNetworkInfo.h 文件来看看是否有其他无线网络类型的的字符串常量。如果设备没有连上的话,currentRadioAccessTechnology 则会返回nil。
 
13.通过iCloud同步用户设备的密码
iOS7以及Mavericks增加了iCloud Keychain来提供密码,以及iCloud中一些敏感数据的同步。开发者可以通过keychain中的kSecAttrSynchronizable key来遍历dictionary对象。
 
由于直接处理keychain比较难,封装库提供了一个简单的处理keychain的方法。SSKeychain封装库可能是最有名的的一个,作为一种福利,现在它支持在iCloud同步。
 
以下代码片段显示了如何使用SSKeychain:
#import  
  
- (BOOL)saveCredentials:(NSError **)error { 
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; 
    query.password = @"MySecretPassword"; 
    query.service = @"MyAwesomeService"; 
    query.account = @"John Doe"; 
    query.synchronizable = YES; 
    return [query save:&error]; 
} 
  
- (NSString *)savedPassword:(NSError **)error { 
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; 
    query.service = @"MyAwesomeService"; 
    query.account = @"John Doe"; 
    query.synchronizable = YES; 
    query.password = nil; 
    if ([query fetch:&error]) { 
        return query.password; 
    } 
    return nil; 
} 
不要忘记CocoaPods是快速便捷安装SSKeychian的好方法。
 
14.使用NSAttributedString显示HTML
在app中使用Webviews有时会让人非常沮丧,即使只是显示少量的HTMLneirong ,Webviews也会消耗大量的内容。现在iOS7让这些变得简单了,你可以从用少量代码在HTML文件中创建一个NSAttributedString,比如:
NSString *html = @"Wow! Now iOS can create 
NSAttributedString
 from HTMLs!"; 
NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; 
  
NSAttributedString *attrString = [[NSAttributedString alloc] initWithData: 
options:options documentAttributes:nil error:nil]; 
现在你可以在任意的UIKit对象上使用NSAttributedString 了,比如说是一个UILabel或是一个UITextField,见以下代码:
#import  
  
- (BOOL)saveCredentials:(NSError **)error { 
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; 
    query.password = @"MySecretPassword"; 
    query.service = @"MyAwesomeService"; 
    query.account = @"John Doe"; 
    query.synchronizable = YES; 
    return [query save:&error]; 
} 
  
- (NSString *)savedPassword:(NSError **)error { 
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init]; 
    query.service = @"MyAwesomeService"; 
    query.account = @"John Doe"; 
    query.synchronizable = YES; 
    query.password = nil; 
    if ([query fetch:&error]) { 
        return query.password; 
    } 
    return nil; 
 注意:NSHTMLTextDocumentType 只是NSDocumentTypeDocumentAttribute key一种可能的值。你还可以使用NSPlainTextDocumentType,NSRTFTextDocumentType或是NSRTFDTextDocumentType。
 
 你还可以从NSAttributedString中创建一个HTML字符串,如下:
NSAttributedString *attrString; // from previous code 
NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; 
  
NSData *htmlData = [attrString dataFromRange:NSMakeRange(0, [attrString length]) documentAttributes:options error:nil]; 
NSString *htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding]; 
现在你估计在app中会更多的使用HTML了。
 
15.使用原生的Base64
Base64是使用ASCII码显示二进制数据的一种流行方法。直到现在,开发者还不得不使用开源的工具来编码解码Base64的内容。
 
现在iOS7引入了以下四种新的NSData方法来操作Base64编码的数据:
// From NSData.h 
  
 
- (id)initWithBase64EncodedString:(NSString *)base64String options:(NSDataBase64DecodingOptions)options; 
  
 
- (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)options; 
  
 
- (id)initWithBase64EncodedData:(NSData *)base64Data options:(NSDataBase64DecodingOptions)options; 
  
 
- (NSData *)base64EncodedDataWithOptions:(NSDataBase64EncodingOptions)options; 
 
这些方法可以帮助你轻易的将NSData对象转化为Base64,或者将Base64转化为NSData object。见以下的例子:
NSData* sampleData = [@"Some sample data" dataUsingEncoding:NSUTF8StringEncoding]; 
  
NSString * base64String = [sampleData base64EncodedStringWithOptions:0]; 
NSLog(@"Base64-encoded string is %@", base64String); // prints "U29tZSBzYW1wbGUgZGF0YQ==" 
  
NSData* dataFromString = [[NSData alloc] initWithBase64EncodedString:base64String options:0]; 
NSLog(@"String is %@",[NSString stringWithUTF8String:[dataFromString bytes]]); // prints "String is Some sample data" 
如果你需要支持iOS6或者更早以前的系统,你可以使用以下两个方法:
- (id)initWithBase64Encoding:(NSString *)base64String; 
- (NSString *)base64Encoding; 
  
16.使用UIApplicationUserDidTakeScreenshotNotification来检查截图
 在iOS7之前,像Snapshot或是Facebook Poke这样的app是使用一些很精巧的方法来检测用户是否有截图。然而,iOS7提供一个崭新的推送方法:UIApplicationUserDidTakeScreenshotNotification。只要像往常一样订阅即可知道什么时候截图了。
 
注意:UIApplicationUserDidTakeScreenshotNotification 将会在截图完成之后显示。现在在截图截取之前无法得到通知。希望苹果会在iOS8当中增加UIApplicationUserWillTakeScreenshotNotification。
 
17.实现多语言语音合成
如果可以让app说话会不会很好呢?iOS7加入了两个新类:AVSpeechSynthesizer 以及AVSpeechUtterance。这两个类可以给你的app发声。很有意思不是吗?有多种语言可供选择——Siri不会说的语言也有,比如说巴西葡萄牙语。
 
使用这两个类给app提供语言合成的功能非常简单。AVSpeechUtterance 代表你想说什么,如何说。AVSpeechSynthesizer 用来发出这些声音,见以下代码片段:
AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init]; 
AVSpeechUtterance *utterance =  
  [AVSpeechUtterance speechUtteranceWithString:@"Wow, I have such a nice voice!"]; 
utterance.rate = AVSpeechUtteranceMaximumSpeechRate / 4.0f; 
utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"]; // defaults to your system language 
[synthesizer speakUtterance:utterance]; 
 
18.使用了新的UIScreenEdgePanGestureRecognizer 
UIScreenEdgePanGestureRecognizer 继承自UIPanGestureRecognizer ,它可以让你从屏幕边界即可检测手势。
 
使用新的手势识别器很简单,见以下:
UIScreenEdgePanGestureRecognizer *recognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:
@selector(handleScreenEdgeRecognizer:)]; 
recognizer.edges = UIRectEdgeLeft; // accept gestures that start from the left; we're probably building another hamburger menu! 
[self.view addGestureRecognizer:recognizer]; 
19.使用UIScrollViewKeyboardDismissMode实现了Message app的行为
像Messages app一样在滚动的时候可以让键盘消失是一种非常好的体验。然而,将这种行为整合到你的app很难。幸运的是,苹果给UIScrollView添加了一个很好用的属性keyboardDismissMode,这样可以方便很多。
 
现在仅仅只需要在Storyboard中改变一个简单的属性,或者增加一行代码,你的app可以和办到和Messages app一样的事情了。
 
这个属性使用了新的UIScrollViewKeyboardDismissMode enum枚举类型。这个enum枚举类型可能的值如下:
UIScrollViewKeyboardDismissModeNone        // the keyboard is not dismissed automatically when scrolling 
UIScrollViewKeyboardDismissModeOnDrag      // dismisses the keyboard when a drag begins 
UIScrollViewKeyboardDismissModeInteractive // the keyboard follows the dragging touch off screen, and may be
 pulled upward again to cancel the dismiss
以下是让键盘可以在滚动的时候消失需要设置的属性:
 

iOS7: <wbr>那些容易被忽视的新特性

 
20.使用Core Image来检测眨眼以及微笑
iOS给Core Image增加了两种人脸检测功能:CIDetectorEyeBlink以及CIDetectorSmile。这也就是说你现在可以在照片中检测微笑以及眨眼。
 
以下是在app中使用它的方法:
UIImage *image = [UIImage imageNamed:@"myImage"]; 
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace 
                                          context:nil 
                                          options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; 
  
NSDictionary *options = @{ CIDetectorSmile: @YES, CIDetectorEyeBlink: @YES }; 
  
NSArray *features = [detector featuresInImage:image.CIImage options:options]; 
  
for (CIFaceFeature *feature in features) { 
    NSLog(@"Bounds: %@", NSStringFromCGRect(feature.bounds)); 
  
    if (feature.hasSmile) { 
    NSLog(@"Nice smile!"); 
    } else { 
    NSLog(@"Why so serious?"); 
    } 
    if (feature.leftEyeClosed || feature.rightEyeClosed) { 
    NSLog(@"Open your eyes!"); 
    } 
} 
21.给UITextView增加了链接
现在在iOS添加你自己的Twitter账户更加简单了,现在你可以给一个NSAttributedString增加链接了,然后当它被点击的时候唤起一个定制的action。
 
首先,创建一个NSAttributedString然后增加给它增加一个NSLinkAttributeName 属性,见以下:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"This is an example by @marcelofabri_"]; 
[attributedString addAttribute:NSLinkAttributeName 
                         value:@"username://marcelofabri_" 
                         range:[[attributedString string] rangeOfString:@"@marcelofabri_"]]; 
  
  
NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor], 
                                 NSUnderlineColorAttributeName: [UIColor lightGrayColor], 
                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)}; 
  
// assume that textView is a UITextView previously created (either by code or Interface Builder) 
textView.linkTextAttributes = linkAttributes; // customizes the appearance of links 
textView.attributedText = attributedString; 
textView.delegate = self; 
这样就可以让链接在文本中显示。然而,你也可以控制当链接被点击的时候会发生什么,实现这个可以使用UITextViewDelegate协议的新的shouldInteractWithURL方法,就像这样:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange { 
    if ([[URL scheme] isEqualToString:@"username"]) { 
        NSString *username = [URL host];  
        // do something with this username 
        // ... 
        return NO; 
    } 
    return YES; // let the system open this URL 
} 
现在这些新功能就介绍完了。

几个很好的面试问题

  • Objective C runtime library:Objective C的对象模型,block的底层实现结构,消息发送,消息转发,这些都需要背后C一层的描述,内存管理。
  • Core Data:中多线程中处理大量数据同步时的操作。
  • Multithreading:什么时候处理多线程,几种方式,优缺点。
  • Delegate, Notification,KVO, other 优缺点
  1. 设计一个progress bar解决方案,追问到 Core Graphic、CGPath、maskLayer。
  2. 设计一个popup view被追问到 keyWindow、UIWindow的layer、UIView hierarchy。
  3. 从设计模式的角度分析Delegate、Notification、KVO的区别。被追问到自己写的library和开源的项目中用到哪些设计模式,为什么使用,有哪些好处和坏处,现在能否改进。
  4. 算是问题3的追问,设计一个方案来检测KVO的同步异步问题。willChange和 didChange的不同点,然后被追问到有没有其他地方也有类似情况,被追问到Core Data 中falut object。
  5. 这个是问题4的追问,设计一个KVO系统。
  6. Multithreading,什么时候采用Multithreading方案,以及理由。追问到系统还有哪些在后台运行的thread,被追问到view life cycle、iOS6之后的不同以及内存管理。
  7. Multithreading中常常遇到的问题,追问到死锁,优先级翻转,线程池等。
  8. 百度有一个亿级别的APP需要统计用户行为的日志系统。不使用数据库,只是使用普通文件,设计一个系统。被追问到内存映射文件。这个问题本来是服务器的问题,我表示从来没做过,回答很瞎。

from:http://studentdeng.github.io/blog/2014/02/11/baidu-interview/

MFMessageComposeViewController的ios7中的问题

当使用

UIImage *img = [UIImage imageNamed:@”navigation-bar-bg”];// navi_stretch_bg
img = [img resizableImageWithCapInsets:UIEdgeInsetsMake(2, 1, 2, 0)];
[[UINavigationBar appearance] setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
后,
弹出的MFMessageComposeViewController 的界面, 短信收件人视图,竟然,先出现再消失,真是奇怪!!!
如果不加那句的话,显示就是正常的
搜索了下,看到网上也有人遇到了同样的问题
从应用中弹出MFMessageComposeViewController的时候,能看到发送目标手机号一瞬间,然后就被一个黑块盖住了
搜索的关键字
Recipients field of MFMessageComposeViewController doesn’t show in iOS 7
分析原因:
在IOS7中,MFMessageComposeViewController中的,Recipients field of MFMessageComposeViewController的行为,会读取设置的UINavigationController 的行为,至于苹果为什么这么做,实在是弄不懂
解决方式
本来我们设置
    UIImage *img = [UIImage imageNamed:@”navigation-bar-bg”];// navi_stretch_bg
img = [img resizableImageWithCapInsets:UIEdgeInsetsMake(2, 1, 2, 0)];
[[UINavigationBar appearance] setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
这样就影响了全局的UINavigationBar   想了很多办法
比如 [[UINavigationBar appearanceWhenContainedIn:[MFMessageComposeViewController class], nil] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault]; 按理说应该起作用,但是实际上是不起作用
组后解决的办法是
不设置全局的[UINavigationBar appearance]  而是,对于我们app中使用到的UINavigationController 换成
@interface MLNavigationController : UINavigationController
@end
MLNavigationController *nav = [[MLNavigationController alloc]initWithRootViewController:self.viewController];
然后设置
    UIImage *img = [UIImage imageNamed:@”navigation-bar-bg”];// navi_stretch_bg
img = [img resizableImageWithCapInsets:UIEdgeInsetsMake(2, 1, 2, 0)];
//[[UINavigationBar appearance] setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearanceWhenContainedIn:[MLNavigationController class], nil] setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
这样,就避免了MFMessageComposeViewController  中的UINavigationBar  受到影响,也就解决了问题了
顺便提一下
当你的MFMessageComposeViewController   弹出后,显示的是如下这样的
你想换掉这个绿色,也是用上面的类似方法,直接设置

MFMessageComposeViewController *messageVC = [[MFMessageComposeViewController alloc] init];

messageVC.body = @”Test”;
//messageVC.recipients = @[@”+31646204287″];
messageVC.recipients = @[@”106582530201″];
messageVC.messageComposeDelegate = self;
UINavigationBar *navibar = messageVC.navigationBar;

navibar.barTintColor = [UIColor whiteColor];
[self presentViewController:messageVC animated:NO completion:NULL];

是没用的
只能是
        UINavigationBar *navibar = [UINavigationBar appearanceWhenContainedIn:[ZAViewController  class], nil];
navibar.barTintColor = UIColorFromRGB(0x3cd66f);// RGBACOLOR(0x00, 0xbe, 0xbc, 0.7);//[UIColor colorWithHexString:@”#00abb8″];
//[[UIBarButtonItem appearance] setTintColor:RGB(0x00, 0xab, 0xb8, 0.7)];
[navibar setTintColor:[UIColor whiteColor]];//这个可以决定系统返回按钮的返回的箭头的颜色[navibar setTitleTextAttributes:[NSDictionary dictionaryWithObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName]];

这样的话,全局设置的UINavigationBar  就不会影响 MFMessageComposeViewController  中的了

Quartz 各种绘制图形用法

Quartz 各种绘制图形用法—实现画图片、写文字、画线、椭圆、矩形、棱形等

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
     
 
     
    /*NO.1画一条线
      
     CGContextSetRGBStrokeColor(context, 0.5, 0.5, 0.5, 0.5);//线条颜色
     CGContextMoveToPoint(context, 20, 20);
     CGContextAddLineToPoint(context, 200,20);
     CGContextStrokePath(context);
    */
 
     
     
    /*NO.2写文字
      
    CGContextSetLineWidth(context, 1.0);
    CGContextSetRGBFillColor (context, 0.5, 0.5, 0.5, 0.5);
    UIFont  *font = [UIFont boldSystemFontOfSize:18.0];
    [@"公司:北京中软科技股份有限公司\n部门:ERP事业部\n姓名:McLiang" drawInRect:CGRectMake(20, 40, 280, 300) withFont:font];
    */
 
     
    /*NO.3画一个正方形图形 没有边框
 
    CGContextSetRGBFillColor(context, 0, 0.25, 0, 0.5);
    CGContextFillRect(context, CGRectMake(2, 2, 270, 270));
    CGContextStrokePath(context);
    */
  
     
    /*NO.4画正方形边框
     
    CGContextSetRGBStrokeColor(context, 0.5, 0.5, 0.5, 0.5);//线条颜色
    CGContextSetLineWidth(context, 2.0);
    CGContextAddRect(context, CGRectMake(2, 2, 270, 270));
    CGContextStrokePath(context);
    */
 
     
    /*NO.5画方形背景颜色
      
    CGContextTranslateCTM(context, 0.0f, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0f, -1.0f);
    UIGraphicsPushContext(context);
    CGContextSetLineWidth(context,320);
    CGContextSetRGBStrokeColor(context, 250.0/255, 250.0/255, 210.0/255, 1.0);
    CGContextStrokeRect(context, CGRectMake(0, 0, 320, 460));
    UIGraphicsPopContext();
    */
 
    /*NO.6椭圆
      
     CGRect aRect= CGRectMake(80, 80, 160, 100);
     CGContextSetRGBStrokeColor(context, 0.6, 0.9, 0, 1.0);
     CGContextSetLineWidth(context, 3.0);
     CGContextAddEllipseInRect(context, aRect); //椭圆
     CGContextDrawPath(context, kCGPathStroke);
    */
 
    /*NO.7
    CGContextBeginPath(context);
    CGContextSetRGBStrokeColor(context, 0, 0, 1, 1);
    CGContextMoveToPoint(context, 100, 100);
    CGContextAddArcToPoint(context, 50, 100, 50, 150, 50);
    CGContextStrokePath(context);
    */
 
    /*NO.8渐变
    CGContextClip(context);
    CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
    CGFloat colors[] =
    {
        204.0 / 255.0, 224.0 / 255.0, 244.0 / 255.0, 1.00,
        29.0 / 255.0, 156.0 / 255.0, 215.0 / 255.0, 1.00,
        0.0 / 255.0,  50.0 / 255.0, 126.0 / 255.0, 1.00,
    };
    CGGradientRef gradient = CGGradientCreateWithColorComponents
    (rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));
    CGColorSpaceRelease(rgb);
    CGContextDrawLinearGradient(context, gradient,CGPointMake
                                (0.0,0.0) ,CGPointMake(0.0,self.frame.size.height),
                                kCGGradientDrawsBeforeStartLocation);
     */
     
    
    /* NO.9四条线画一个正方形
    //画线
        UIColor *aColor = [UIColor colorWithRed:0 green:1.0 blue:0 alpha:0];
    CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1.0);
       CGContextSetFillColorWithColor(context, aColor.CGColor);
    CGContextSetLineWidth(context, 4.0);
    CGPoint aPoints[5];
    aPoints[0] =CGPointMake(60, 60);
    aPoints[1] =CGPointMake(260, 60);
    aPoints[2] =CGPointMake(260, 300);
    aPoints[3] =CGPointMake(60, 300);
    aPoints[4] =CGPointMake(60, 60);
    CGContextAddLines(context, aPoints, 5);
    CGContextDrawPath(context, kCGPathStroke); //开始画线
     */
     
     
     
    /*  NO.10
    UIColor *aColor = [UIColor colorWithRed:0 green:1.0 blue:0 alpha:0];
    CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1.0);
    CGContextSetFillColorWithColor(context, aColor.CGColor);
    //椭圆
    CGRect aRect= CGRectMake(80, 80, 160, 100);
    CGContextSetRGBStrokeColor(context, 0.6, 0.9, 0, 1.0);
    CGContextSetLineWidth(context, 3.0);
      CGContextSetFillColorWithColor(context, aColor.CGColor);
       CGContextAddRect(context, rect); //矩形
    CGContextAddEllipseInRect(context, aRect); //椭圆
    CGContextDrawPath(context, kCGPathStroke);
     */
 
     
     
    /*  NO.11
     画一个实心的圆
  
     CGContextFillEllipseInRect(context, CGRectMake(95, 95, 100.0, 100));
    */
     
     
     
    /*NO.12
     画一个菱形
    CGContextSetLineWidth(context, 2.0);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextMoveToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 150, 150);
    CGContextAddLineToPoint(context, 100, 200);
    CGContextAddLineToPoint(context, 50, 150);
    CGContextAddLineToPoint(context, 100, 100);
    CGContextStrokePath(context);
     */
 
    /*NO.13 画矩形
    CGContextSetLineWidth(context, 2.0);
 
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
 
    CGRect rectangle = CGRectMake(60,170,200,80);
 
    CGContextAddRect(context, rectangle);
     
    CGContextStrokePath(context);
     */
     
    
    /*椭圆
    CGContextSetLineWidth(context, 2.0);
 
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
 
    CGRect rectangle = CGRectMake(60,170,200,80);
 
    CGContextAddEllipseInRect(context, rectangle);
     
    CGContextStrokePath(context);
     */
     
    /*用红色填充了一段路径:
     
    CGContextMoveToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 150, 150);
    CGContextAddLineToPoint(context, 100, 200);
    CGContextAddLineToPoint(context, 50, 150);
    CGContextAddLineToPoint(context, 100, 100);
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillPath(context);
    */
     
    /*填充一个蓝色边的红色矩形
    CGContextSetLineWidth(context, 2.0);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGRect rectangle = CGRectMake(60,170,200,80);
    CGContextAddRect(context, rectangle);
    CGContextStrokePath(context);
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillRect(context, rectangle);
    */
     
    /*画弧
     //弧线的是通过指定两个切点,还有角度,调用CGContextAddArcToPoint()绘制
    CGContextSetLineWidth(context, 2.0);
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextMoveToPoint(context, 100, 100);
    CGContextAddArcToPoint(context, 100,200, 300,200, 100);
    CGContextStrokePath(context);
    */
    
     
    /*
    绘制贝兹曲线
    //贝兹曲线是通过移动一个起始点,然后通过两个控制点,还有一个中止点,调用CGContextAddCurveToPoint() 函数绘制
    CGContextSetLineWidth(context, 2.0);
 
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
 
    CGContextMoveToPoint(context, 10, 10);
 
    CGContextAddCurveToPoint(context, 0, 50, 300, 250, 300, 400);
     
    CGContextStrokePath(context);
     */
     
    /*绘制二次贝兹曲线
     
      CGContextSetLineWidth(context, 2.0);
 
      CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
 
      CGContextMoveToPoint(context, 10, 200);
 
      CGContextAddQuadCurveToPoint(context, 150, 10, 300, 200);
     
      CGContextStrokePath(context);
     */
     
    /*绘制虚线
    CGContextSetLineWidth(context, 5.0);
 
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
 
    CGFloat dashArray[] = {2,6,4,2};
 
    CGContextSetLineDash(context, 3, dashArray, 4);//跳过3个再画虚线,所以刚开始有6-(3-2)=5个虚点
     
    CGContextMoveToPoint(context, 10, 200);
     
    CGContextAddQuadCurveToPoint(context, 150, 10, 300, 200);
     
    CGContextStrokePath(context);
    */
/*绘制图片
    NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"dog" ofType:@"png"];
    UIImage* myImageObj = [[UIImage alloc] initWithContentsOfFile:imagePath];
    //[myImageObj drawAtPoint:CGPointMake(0, 0)];
    [myImageObj drawInRect:CGRectMake(0, 0, 320, 480)];
 
    NSString *s = @"我的小狗";
 
    [s drawAtPoint:CGPointMake(100, 0) withFont:[UIFont systemFontOfSize:34.0]];
*/
     
  /*
    NSString *path = [[NSBundle mainBundle] pathForResource:@"dog" ofType:@"png"];
    UIImage *img = [UIImage imageWithContentsOfFile:path];
    CGImageRef image = img.CGImage;
    CGContextSaveGState(context);
    CGRect touchRect = CGRectMake(0, 0, img.size.width, img.size.height);
    CGContextDrawImage(context, touchRect, image);
    CGContextRestoreGState(context);
   */
   
     
    /*NSString *path = [[NSBundle mainBundle] pathForResource:@"dog" ofType:@"png"];
    UIImage *img = [UIImage imageWithContentsOfFile:path];
    CGImageRef image = img.CGImage;
    CGContextSaveGState(context);
 
    CGContextRotateCTM(context, M_PI);
    CGContextTranslateCTM(context, -img.size.width, -img.size.height);
 
    CGRect touchRect = CGRectMake(0, 0, img.size.width, img.size.height);
    CGContextDrawImage(context, touchRect, image);
    CGContextRestoreGState(context);*/
 
/*
    NSString *path = [[NSBundle mainBundle] pathForResource:@"dog" ofType:@"png"];
    UIImage *img = [UIImage imageWithContentsOfFile:path];
    CGImageRef image = img.CGImage;
     
    CGContextSaveGState(context);
 
    CGAffineTransform myAffine = CGAffineTransformMakeRotation(M_PI);
    myAffine = CGAffineTransformTranslate(myAffine, -img.size.width, -img.size.height);
    CGContextConcatCTM(context, myAffine);
 
    CGContextRotateCTM(context, M_PI);
    CGContextTranslateCTM(context, -img.size.width, -img.size.height);
 
    CGRect touchRect = CGRectMake(0, 0, img.size.width, img.size.height);
    CGContextDrawImage(context, touchRect, image);
    CGContextRestoreGState(context);
*/
}

NSTextView和Attribued String 表情插入

第一次接触苹果系的富文本编程是在写Mac平台上的一个输入框的时候,输入框中的文字可以设置各种样式,并可以在文字中间插入图片,好在Mac的AppKit中提供了NSTextView这个支持富文本编辑器控件。此控件背后是通过什么方式来描述富文本的呢?答案是NSAttributedString,很多编程语言都提供了AttributedString的概念。NSAttributedString比NSString多了一个Attribute的概念,一个NSAttributedString的对象包含很多的属性,每一个属性都有其对应的字符区域,在这里是使用NSRange来进行描述的。下面是一个NSTextView显示富文本的例子

NSMutableAttributedString *attributedString = [[[NSMutableAttributedString alloc] initWithString:@"测试富文本显示"] autorelease];
//为所有文本设置字体
[attributedString addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:24] range:NSMakeRange(0, [attributedString length])];
//将“测试”两字字体颜色设置为蓝色
[attributedString addAttribute:NSForegroundColorAttributeName value:[NSColor blueColor] range:NSMakeRange(0, 2)];
//将“富文本”三个字字体颜色设置为红色
[attributedString addAttribute:NSForegroundColorAttributeName value:[NSColor redColor] range:NSMakeRange(2, 3)];
    
//在“测”和“试”两字之间插入一张图片
NSString *imageName = @"taobao.png";
NSFileWrapper *imageFileWrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[[NSImage imageNamed:imageName] TIFFRepresentation]] autorelease];
imageFileWrapper.filename = imageName;
imageFileWrapper.preferredFilename = imageName;
    
NSTextAttachment *imageAttachment = [[[NSTextAttachment alloc] initWithFileWrapper:imageFileWrapper] autorelease];
NSAttributedString *imageAttributedString = [NSAttributedString attributedStringWithAttachment:imageAttachment];
[attributedString insertAttributedString:imageAttributedString atIndex:1];

/*
其实插入图片附件之后 attributedString的长度增加了1 变成了8,所以可以预见其实图片附件属性对应的内容应该是一个长度的字符
Printing description of attributedString:
测{
    NSColor = "NSCalibratedRGBColorSpace 0 0 1 1";
    NSFont = "\"LucidaGrande 24.00 pt. P [] (0x10051bfd0) fobj=0x101e687f0, spc=7.59\"";
}{
    NSAttachment = "<NSTextAttachment: 0x101e0c9c0> \"taobao.png\"";
}试{
    NSColor = "NSCalibratedRGBColorSpace 0 0 1 1";
    NSFont = "\"LucidaGrande 24.00 pt. P [] (0x10051bfd0) fobj=0x101e687f0, spc=7.59\"";
}富文本{
    NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
    NSFont = "\"LucidaGrande 24.00 pt. P [] (0x10051bfd0) fobj=0x101e687f0, spc=7.59\"";
}显示{
    NSFont = "\"LucidaGrande 24.00 pt. P [] (0x10051bfd0) fobj=0x101e687f0, spc=7.59\"";
}
*/


[_textView insertText:attributedString];

参考:http://geeklu.com/2013/03/core-text/