常见问题html
昨天到目前为止学习了图片,以及视频上传的压缩处理,还有就是instruments上面的一些用法,还有一个自动化测试工具,进行软件的测试处理
常见问题ios
你见过妹子不爱漂亮妆爱code的吗,因此对软件开发是一种热爱,在心情很差或是很烦躁的时候看看code或是研究一下code就会心情比较好一些git
这个对于如今的我一个初级ios开发说,可能目前仍是download一些别人的代码进行研究和借鉴github
首先是整个项目web
熟悉使用过CocoaPods使用,以前 有一个项目中使用的是pods,具体见笔记sql
首先Oauth受权的原理知识就再也不赘述
以微信的第三方受权来讲微信登陆受权开发数据库
//向微信注册
[WXApi registerApp:kWXAPP_ID withDescription:@”weixin”];编程
//受权后回调 WXApiDelegate
-(void)onResp:(BaseReq *)resp
{
/*
ErrCode ERR_OK = 0(用户赞成)
ERR_AUTH_DENIED = -4(用户拒绝受权)
ERR_USER_CANCEL = -2(用户取消)
code 用户换取access_token的code,仅在ErrCode为0时有效
state 第三方程序发送时用来标识其请求的惟一性的标志,由第三方程序调用sendReq时传入,由微信终端回传,state字符串长度不能超过1K
lang 微信客户端当前语言
country 微信用户当前国家信息
*/
SendAuthResp aresp = (SendAuthResp )resp;
if (aresp.errCode== 0) {
NSString *code = aresp.code;
NSDictionary *dic = @{@”code”:code};
}
}设计模式
//和QQ,新浪并列回调句柄
- (BOOL)application:(UIApplication )application openURL:(NSURL )url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [TencentOAuth HandleOpenURL:url] ||
[WeiboSDK handleOpenURL:url delegate:self] ||
[WXApi handleOpenURL:url delegate:self];;
}api
(BOOL)application:(UIApplication )application handleOpenURL:(NSURL )url
{
return [TencentOAuth HandleOpenURL:url] ||
[WeiboSDK handleOpenURL:url delegate:self] ||
[WXApi handleOpenURL:url delegate:self];;
}
下面用代码来实现:
第一步:code
-(void)sendAuthRequest
{
SendAuthReq* req =[[SendAuthReq alloc ] init];
req.scope = @”snsapi_userinfo,snsapi_base”;
req.state = @”0744” ;
[WXApi sendReq:req];
}
这里获取后会调用以前在AppDelegate里面的对应oauthResp回调,得到获得的code。
第二步:token和openid
-(void)getAccess_token
{
//https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
NSString *url =[NSString stringWithFormat:@"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code",kWXAPP_ID,kWXAPP_SECRET,self.wxCode.text]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *zoneUrl = [NSURL URLWithString:url]; NSString *zoneStr = [NSString stringWithContentsOfURL:zoneUrl encoding:NSUTF8StringEncoding error:nil]; NSData *data = [zoneStr dataUsingEncoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(), ^{ if (data) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; /* { "access_token" = "OezXcEiiBSKSxW0eoylIeJDUKD6z6dmr42JANLPjNN7Kaf3e4GZ2OncrCfiKnGWiusJMZwzQU8kXcnT1hNs_ykAFDfDEuNp6waj-bDdepEzooL_k1vb7EQzhP8plTbD0AgR8zCRi1It3eNS7yRyd5A"; "expires_in" = 7200; openid = oyAaTjsDx7pl4Q42O3sDzDtA7gZs; "refresh_token" = "OezXcEiiBSKSxW0eoylIeJDUKD6z6dmr42JANLPjNN7Kaf3e4GZ2OncrCfiKnGWi2ZzH_XfVVxZbmha9oSFnKAhFsS0iyARkXCa7zPu4MqVRdwyb8J16V8cWw7oNIff0l-5F-4-GJwD8MopmjHXKiA"; scope = "snsapi_userinfo,snsapi_base"; } */ self.access_token.text = [dic objectForKey:@"access_token"]; self.openid.text = [dic objectForKey:@"openid"]; } }); });
}
利用GCD来获取对应的token和openID.
第三步:userinfo
-(void)getUserInfo
{
// https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
NSString *url =[NSString stringWithFormat:@"https://api.weixin.qq.com/sns/userinfo?access_token=%@&openid=%@",self.access_token.text,self.openid.text]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *zoneUrl = [NSURL URLWithString:url]; NSString *zoneStr = [NSString stringWithContentsOfURL:zoneUrl encoding:NSUTF8StringEncoding error:nil]; NSData *data = [zoneStr dataUsingEncoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(), ^{ if (data) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; /* { city = Haidian; country = CN; headimgurl = "http://wx.qlogo.cn/mmopen/FrdAUicrPIibcpGzxuD0kjfnvc2klwzQ62a1brlWq1sjNfWREia6W8Cf8kNCbErowsSUcGSIltXTqrhQgPEibYakpl5EokGMibMPU/0"; language = "zh_CN"; nickname = "xxx"; openid = oyAaTjsDx7pl4xxxxxxx; privilege = ( ); province = Beijing; sex = 1; unionid = oyAaTjsxxxxxxQ42O3xxxxxxs; } */ self.nickname.text = [dic objectForKey:@"nickname"]; self.wxHeadImg.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[dic objectForKey:@"headimgurl"]]]]; } }); });
}
执行到这一步就算完成了整个受权登陆的功能,能把昵称和头像显示出来,剩下的就是及时刷新你的token
在开发iOS程序时,有时候须要将时间格式调整成本身但愿的格式,这个时候咱们能够用NSDateFormatter类来处理。例如:
//实例化一个NSDateFormatter对象
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
//设定时间格式,这里能够设置成本身须要的格式
[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];
//用[NSDate date]能够获取系统当前时间
NSString *currentDateStr = [dateFormatter stringFromDate:[NSDate date]];
//输出格式为:2010-10-27 10:22:13
NSLog(@”%@”,currentDateStr);
//alloc后对不使用的对象别忘了release
[dateFormatter release];
1、字符说明
(:)
时间分隔符。在某些区域设置中,可使用其余字符表示时间分隔符。时间分隔符在格式化时间值时分隔小时、分钟和秒。格式化输出中用做时间分隔符的实际字符由您的应用程序的当前区域性值肯定。
(/)
日期分隔符。在某些区域设置中,可使用其余字符表示日期分隔符。日期分隔符在格式化日期值时分隔日、月和年。格式化输出中用做日期分隔符的实际字符由您的应用程序的当前区域性肯定。
(%)
用于代表不论尾随什么字母,随后字符都应该以单字母格式读取。也用于代表单字母格式应以用户定义格式读取。有关更多详细信息,请参见下面的内容。
d
将日显示为不带前导零的数字(如 1)。若是这是用户定义的数字格式中的惟一字符,请使用 %d。
dd
将日显示为带前导零的数字(如 01)。
EEE
将日显示为缩写形式(例如 Sun)。
EEEE
将日显示为全名(例如 Sunday)。
M
将月份显示为不带前导零的数字(如一月表示为 1)。若是这是用户定义的数字格式中的惟一字符,请使用 %M。
MM
将月份显示为带前导零的数字(例如 01/12/01)。
MMM
将月份显示为缩写形式(例如 Jan)。
MMMM
将月份显示为完整月份名(例如 January)。
gg
显示时代/纪元字符串(例如 A.D.)
h
使用 12 小时制将小时显示为不带前导零的数字(例如 1:15:15 PM)。若是这是用户定义的数字格式中的惟一字符,请使用 %h。
hh
使用 12 小时制将小时显示为带前导零的数字(例如 01:15:15 PM)。
H
使用 24 小时制将小时显示为不带前导零的数字(例如 1:15:15)。若是这是用户定义的数字格式中的惟一字符,请使用 %H。
HH
使用 24 小时制将小时显示为带前导零的数字(例如 01:15:15)。
m
将分钟显示为不带前导零的数字(例如 12:1:15)。若是这是用户定义的数字格式中的惟一字符,请使用 %m。
mm
将分钟显示为带前导零的数字(例如 12:01:15)。
s
将秒显示为不带前导零的数字(例如 12:15:5)。若是这是用户定义的数字格式中的惟一字符,请使用 %s。
ss
将秒显示为带前导零的数字(例如 12:15:05)。
f
显示秒的小数部分。例如,ff 将精确显示到百分之一秒,而 ffff 将精确显示到万分之一秒。用户定义格式中最多可以使用七个 f 符号。若是这是用户定义的数字格式中的惟一字符,请使用 %f。
t
使用 12 小时制,并对中午以前的任一小时显示大写的 A,对中午到 11:59 P.M 之间的任一小时显示大写的 P。若是这是用户定义的数字格式中的惟一字符,请使用 %t。
tt
对于使用 12 小时制的区域设置,对中午以前任一小时显示大写的 AM,对中午到 11:59 P.M 之间的任一小时显示大写的 PM。
对于使用 24 小时制的区域设置,不显示任何字符。
y
将年份 (0-9) 显示为不带前导零的数字。若是这是用户定义的数字格式中的惟一字符,请使用 %y。
yy
以带前导零的两位数字格式显示年份(若是适用)。
yyy
以四位数字格式显示年份。
yyyy
以四位数字格式显示年份。
z
显示不带前导零的时区偏移量(如 -8)。若是这是用户定义的数字格式中的惟一字符,请使用 %z。
zz
显示带前导零的时区偏移量(例如 -08)
zzz
显示完整的时区偏移量(例如 -08:00)
格式显示
M/d/yy
12/7/58
d-MMM
7-Dec
d-MMMM-yy
7-December-58
d MMMM
7 December
MMMM yy
December 58
hh:mm tt
08:50 PM
h:mm:ss t
8:50:35 P
H:mm
20:50
H:mm:ss
20:50:35
M/d/yyyy H:mm
12/7/1958 20:50
2、NSDate日期操做总结
1 // 当前时间建立NSDate
NSDate *myDate = [NSDate date]; NSLog(@"myDate = %@",myDate);
2 //从如今开始的24小时
NSTimeInterval secondsPerDay = 24*60*60; NSDate *tomorrow = [NSDate dateWithTimeIntervalSinceNow:secondsPerDay]; NSLog(@"myDate = %@",tomorrow);
3//根据已有日期建立日期
NSTimeInterval secondsPerDay1 = 24*60*60; NSDate *now = [NSDate date]; NSDate *yesterDay = [now addTimeInterval:-secondsPerDay1]; NSLog(@"yesterDay = %@",yesterDay);
4//比较日期
BOOL sameDate = [now isEqualToDate:yesterDay]; NSLog(@"sameDate = %lu",sameDate); 4.1//获取较早的日期 NSDate *earlierDate = [yesterDay earlierDate:now]; NSLog(@"earlierDate = %@",earlierDate); 4.2//较晚的日期 NSDate *laterDate = [yesterDay laterDate:now]; NSLog(@"laterDate = %@",laterDate); //两个日期之间相隔多少秒 NSTimeInterval secondsBetweenDates= [yesterDay timeIntervalSinceDate:now]; NSLog(@"secondsBetweenDates= %lf",secondsBetweenDates); //经过NSCALENDAR类来建立日期 NSDateComponents *comp = [[NSDateComponentsalloc]init]; [comp setMonth:06]; [comp setDay:01]; [comp setYear:2001]; NSCalendar *myCal = [[NSCalendaralloc]initWithCalendarIdentifier:NSGregorianCalendar]; NSDate *myDate1 = [myCal dateFromComponents:comp]; NSLog(@"myDate1 = %@",myDate1); //从已有日期获取日期 unsigned units = NSMonthCalendarUnit|NSDayCalendarUnit|NSYearCalendarUnit; NSDateComponents *comp1 = [myCal components:units fromDate:now]; NSInteger month = [comp1 month]; NSInteger year = [comp1 year]; NSInteger day = [comp1 day]; //NSDateFormatter实现日期的输出 NSDateFormatter *formatter = [[NSDateFormatteralloc]init]; [formatter setDateStyle:NSDateFormatterFullStyle];//直接输出的话是机器码 //或者是手动设置样式[formatter setDateFormat:@"yyyy-mm-dd"]; NSString *string = [formatter stringFromDate:now]; NSLog(@"string = %@",string); NSLog(@"formater = %@",formatter);
//获取日期格式对象
if (dateFormatter == nil) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
}
return dateFormatter;
}
3、NSDate与NSDateFormatter的相关用法
1.NSDateFormatter配合NSDate与NSString之间的转化
NSDateFormatter有下面2个方法:
(NSString )stringFromDate:(NSDate )date;//NSDate转NSString
(NSDate )dateFromString:(NSString )string;//NSString转NSDate
e.g.
NSString *dateString=@”1900-01-01”;
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@”yyyy-MM-dd”];
NSDate *date=[dateFormatter dateFromString:dateString];
[dateFormatter release];
NSString转NSDate与上面相似
NSString *dateString=[dateFormatter dateFromString:[NSDate date]];
2.NSDateFormatter 的一些格式介绍
[dateFormatter setDateFormat:@”yyyy年MM月dd日#EEEE”];EEEE为星期几,EEE为周几
[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];
[dateFormatter setDateFormat:@”yyyy年MMMMd日”];//MMMM 为xx月,一个d能够省去01日前的0
3.NSString转NSDate
用下面这种格式化方式
1)
[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];
2)
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:[NSDate date]];
NSDate *todayDate = [calendar dateFromComponents:components];
4.计算两个时间相差的年/月/日/时/分/秒
//假设目标的时间格式为:2016-11-18T12:15:00.000Z
NSString *yearStr = [_countdownTimeStr substringToIndex:4];
NSString *monthStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(5,2)];
NSString *dayStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(8, 2)];
NSString *hourStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(11,2)];
NSString *minuteStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(14, 2)];
NSString *secondStr = [_countdownTimeStrsubstringWithRange:NSMakeRange(17, 2)];
NSCalendar *cal = [NSCalendarcurrentCalendar];//定义一个NSCalendar对象
NSDateComponents *endDate = [[NSDateComponentsalloc] init];//初始化目标时间
[endDate setYear:[yearStr integerValue]];
[endDate setMonth:[monthStr integerValue]];
[endDate setDay:[dayStr integerValue]];
[endDate setHour:[hourStr integerValue]];
[endDate setMinute:[minuteStr integerValue]];
[endDate setSecond:[secondStr integerValue]];
//也可使用字符串转date(NSDate *date=[dateFormatter dateFromString:dateString];)
NSDate *targetDate = [cal dateFromComponents:endDate];//把目标时间装载入date
NSDate *today = [NSDate date];//获得当前时间
//用来获得具体的时差
unsignedint unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit |NSSecondCalendarUnit;
NSDateComponents *d = [cal components:unitFlags fromDate:todaytoDate:targetDate options:0];
相差(返回类型NSInteger)
[d year] ——>年
[d month] —->月
[d day] —>日
[d hour] —>小时
[d minute] —>分钟
[d second] —>秒
5.世界标准时间UTC /GMT 转为当前系统时区对应的时间
{ //设置源日期时区
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:@”UTC”
];//或GMT
//设置转换后的目标日期时区
//获得源日期与世界标准时间的偏移量
//目标日期与本地时区的偏移量
//获得时间偏移量的差值
NSDate* destinationDateNow = [[[NSDate alloc] initWithTimeInterval:interval sinceDate:anyDate] autorelease];
return
destinationDateNow;
}
例子演示:个人机器是北京时区东八区。
//2013-08-03T12:53:51+0800 UTC时间格式下的北京时间,能够看到北京时间= UTC + 8小时。 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; NSDate *localDate = [dateFormatter dateFromString:@"2013-08-03T04:56:52+0000"]; +0000 表示的是当前时间是个世界时间。 [dateFormatter release]; NSLog(@"now Time = %@",[self getNowDateFromatAnDate:localDate]);
结果:
2013-08-03 12:57:33.391 xxxx[2547:c07] now Time = 2013-08-03 12:56:52 +0000
以上注意一点,在转出来后带的时间是原参数anydate的时区,所以切不可再用NSDateFormatter 转换。不然会多增长一个时区的时间值。应该使用以下来提取字符串
NSString *str = [NSString stringWithFormat:@"%@",[self getNowDateFromatAnDate:localDate]]; NSLog(@"str = %@",str);
注NSDate对象存放的日期始终是UTC的标准时间,能够根据这个时间进行其它时间的转换。所以上面算出来的时间中时区为 +0000,若是此时再转为字符串
几个转换函数
//NSString 2 NSDate
{
NSDateFormatter *dateFormatter = [[NSDateFormatteralloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSDate *retdate = [dateFormatter dateFromString:strdate]; [dateFormatter release]; return retdate;
}
//NSDate 2 NSString
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString *strDate = [dateFormatter stringFromDate:date]; [dateFormatter release]; return strDate;
}
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; [dateFormatter setTimeZone:timeZone]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"]; NSString *dateString = [dateFormatter stringFromDate:localDate]; [dateFormatter release]; return dateString;
}
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; NSTimeZone *timeZone = [NSTimeZone localTimeZone]; [dateFormatter setTimeZone:timeZone]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"]; NSDate *ldate = [dateFormatter dateFromString:utc]; [dateFormatter release]; return ldate;
}
//以上注意字符串时的输入参数的格式,别外不要用%@来查看NSDate的值,由于自己存的就是UTC ,当心被误导。将日期转换成字符串来查看一下。
6.NSDateFormatter格式详细列表一览
NSDateFormatter是一个很经常使用的类,用于格式化NSDate对象,支持本地化的信息。与时间相关的功能还可能会用到NSDateComponents类和NSCalendar类等。
下面主要列出NSDateFormatter常见用法。
NSDate对象包含两个部分,日期(Date)和时间(Time)。格式化的时间字符串主要也是针对日期和时间的。[如下代码中开启了ARC,因此没有release。]
1)基础用法
1 NSDate* now = [NSDate date];
2 NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
3 fmt.dateStyle = kCFDateFormatterShortStyle;
4 fmt.timeStyle = kCFDateFormatterShortStyle;
5 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”en_US”];
6 NSString* dateString = [fmt stringFromDate:now];
7 NSLog(@”%@”, dateString);
打印输出:10/29/12, 2:27 PM
这使用的系统提供的格式化字符串,经过 fmt.dateStyle 和 fmt.timeStyle 进行的设置。实例中使用的参数是 kCFDateFormatterShortStyle,此外还有:
typedef CF_ENUM(CFIndex, CFDateFormatterStyle) { // date and time format styles
kCFDateFormatterNoStyle = 0, // 无输出 kCFDateFormatterShortStyle = 1, // 10/29/12, 2:27 PM kCFDateFormatterMediumStyle = 2, // Oct 29, 2012, 2:36:59 PM kCFDateFormatterLongStyle = 3, // October 29, 2012, 2:38:46 PM GMT+08:00 kCFDateFormatterFullStyle = 4 // Monday, October 29, 2012, 2:39:56 PM China Standard Time
};
2) 自定义区域语言如上实例中,咱们使用的是区域语言是 en_US,指的是美国英语。若是咱们换成简体中文,则代码是:
1 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh_CN”];
则对应的输出为:
typedef CF_ENUM(CFIndex, CFDateFormatterStyle) { // date and time format styles
kCFDateFormatterNoStyle = 0, // 无输出 kCFDateFormatterShortStyle = 1, // 12-10-29 下午2:52 kCFDateFormatterMediumStyle = 2, // 2012-10-29 下午2:51:43 kCFDateFormatterLongStyle = 3, // 2012年10月29日 GMT+0800下午2时51分08秒 kCFDateFormatterFullStyle = 4 // 2012年10月29日星期一 中国标准时间下午2时46分49秒
};
世界通用的区域语言代码,详见 International Components for Unicode (ICU)
3.)自定义日期时间格式NSDateFormatter提供了自定义日期时间的方法,主要是经过设置属性 dateFormat,常见的设置以下:
1 NSDate* now = [NSDate date];
2 NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
3 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh_CN”];
4 fmt.dateFormat = @”yyyy-MM-dd’T’HH:mm:ss”;
5 NSString* dateString = [fmt stringFromDate:now];
6 NSLog(@”%@”, dateString);
打印输出:2012-10-29T16:08:40
结合设置Locale,还能够打印出本地化的字符串信息。
1 NSDate* now = [NSDate date];
2 NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
3 fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh_CN”];
4 fmt.dateFormat = @”yyyy-MM-dd a HH:mm:ss EEEE”;
5 NSString* dateString = [fmt stringFromDate:now];
6 NSLog(@”\n%@”, dateString);
打印输出:2012-10-29 下午 16:25:27 星期一
注:
//快捷设置为手机当前的区域语言
NSDateFormatter *formatter = [[
NSDateFormatteralloc] init];
[formatter
setLocale:[NSLocalecurrentLocale]];
[formatter
setDateFormat
:
@”yyyy-MM-dd HH:mm:ss”];
//+ (
NSLocale
*)currentLocale;
// an object representing the user’s current locale
//+ (
NSLocale
*)systemLocale;
// the default generic root locale with little localization
4)自定义月份星期等字符NSDateFormatter中一样提供了相应的方式,去修改这些字符。通常状况下,使用相应区域语言下面的默认字符就OK了。可是你的确有这个需求,那么也是能够办到的。相应的方法很是多,以下:
Managing AM and PM Symbols
– AMSymbol
– setAMSymbol:
– PMSymbol
– setPMSymbol:
Managing Weekday Symbols
– weekdaySymbols
– setWeekdaySymbols:
– shortWeekdaySymbols
– setShortWeekdaySymbols:
– veryShortWeekdaySymbols
– setVeryShortWeekdaySymbols:
– standaloneWeekdaySymbols
– setStandaloneWeekdaySymbols:
– shortStandaloneWeekdaySymbols
– setShortStandaloneWeekdaySymbols:
– veryShortStandaloneWeekdaySymbols
– setVeryShortStandaloneWeekdaySymbols:
Managing Month Symbols
– monthSymbols
– setMonthSymbols:
– shortMonthSymbols
– setShortMonthSymbols:
– veryShortMonthSymbols
– setVeryShortMonthSymbols:
– standaloneMonthSymbols
– setStandaloneMonthSymbols:
– shortStandaloneMonthSymbols
– setShortStandaloneMonthSymbols:
– veryShortStandaloneMonthSymbols
– setVeryShortStandaloneMonthSymbols:
Managing Quarter Symbols
– quarterSymbols
– setQuarterSymbols:
– shortQuarterSymbols
– setShortQuarterSymbols:
– standaloneQuarterSymbols
– setStandaloneQuarterSymbols:
– shortStandaloneQuarterSymbols
– setShortStandaloneQuarterSymbols:
Managing Era Symbols
– eraSymbols
– setEraSymbols:
– longEraSymbols
– setLongEraSymbols:
7.NSLocale与国际化处理
本地化封装了关于语言,文化以及技术约定和规范的信息。用于提供于用户所处地域相关的定制化信息和首选项信息的设置。经过获取用户的本地化信息设置,咱们能够为用户提供更加友好人性化的界面设置,包括更改应用程序的界面的语言,货币类型,数字,日期格式的格式化,提供正确的地理位置显示等等。IOS内置为应用程序的开发提供了很好的本地化机制,良好的本地化意味着应用程序能够为更多的用户提供服务。其中NSLocale类的的主要做用即是用来封装本地化相关的各类信息,下面简单列举下NSLocale的一些方法,但NSLocale更可能是使用在对数字,时间日期本地化的处理的过程。
1.建立本地化对象
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@”en_US”];
[NSLocale currentLocale]
2.获取系统本地化信息
[NSLocale availableLocaleIdentifiers] ;
[NSLocale ISOCountryCodes] ;
[NSLocale ISOCurrencyCodes] ;
[NSLocale ISOLanguageCodes] ;
3.获取当前系统设置语言的标识符
[[NSLocale currentLocale] localeIdentifier];
[[NSLocale currentLocale] objectForKey:NSLocaleIdentifier];
4.获取本地化对象的具体内容
NSLocale *local = [NSLocale currentLocale];
[local objectForKey:NSLocaleIdentifier];
[local objectForKey: NSLocaleLanguageCode];
key值参见NSLocale Calendar Keys
5.获取当前语言的排版方向和字符方向
[NSLocale lineDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode];
[NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode] ;
6.获取用户的语言偏好设置列表,该列表对应于IOS中Setting>General>Language弹出的面板中的语言列表。
[NSLocale preferredLanguages]
第一个元素即为当前用户设置的语言
7.监听用户本地化设置的消息
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(localChangedHandler:)
name:NSCurrentLocaleDidChangeNotification object:nil];
8.以本地化方式获取国际化信息的显示名称
NSLocale *curLocal = [[NSLocale alloc]initWithLocaleIdentifier:@”zh-Hans”] ;
NSLog(@”%@”,[curLocal displayNameForKey:NSLocaleIdentifier value:@”fr_FR”] );
curLocal = [[NSLocale alloc]initWithLocaleIdentifier:@”zh-Hant”] ;
NSLog(@”%@”,[curLocal displayNameForKey:NSLocaleIdentifier value:@”fr_FR”] );
若你只开发中国区的应用,须要保证用户修改当前语言环境时应用的显示不发生变化。而像NSDateFormatter这样的类,会根据设备的设置,自动返回不一样语言的数据。为了保证返回数据的语言一致,咱们须要设置NSLocale。
1
2 NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@”zh”];
3 NSDateFormatter *secondDateFormatter = [[NSDateFormatter alloc] init];
4 [secondDateFormatter setDateFormat:@”cccc”];
5 secondDateFormatter.locale = locale;
6 NSDate *date = [NSDate date];
7 NSLog(@”%@”, [secondDateFormatter stringFromDate:date]);
固然,像上面的需求很罕见。
做为你们都不经常使用的一个类,NSLocale类是将与国家和语言相关的信息进行简单的组合,包括货币,文学方面的信息。
货币:货币的国际名称(人民币的国际货币名称是CNY);货币符号(人民币的国际货币符号是¥)
文学:标点符号,文字的书写顺序(左右顺序),引用的起止符号等等
若作金融一类的应用可能会用到NSLocale这个类。
这个类稍微了解便可。
有一个instruments的记录笔记,请注意查看
关于iOS技术
苹果iOS 8系统为咱们带来了一个新功能——Handoff,该功能可让 iPhone、iPad 和 Mac 电脑协同工做,即便运行的不是同一操做系统,苹果也能够借此巧妙地链接了用户的多个设备。固然,使用该功能以前,你的设备必须登陆到同一个Apple ID,同时须要都处在蓝牙的可侦测范围内。知足上述条件后,Handoff 便自动就绪并默默工做,等待你随时使用它。
也许,还有一些用户对于这项新功能存在疑问,好比同步传输数据的Handoff安全吗,咱们就来经过如下5句话来了解一下。
1.你的 Handoff 只属于你,Apple ID 是惟一身份标识
你的 Apple ID(也同时是你的 iCloud 和 iTunes 帐户)被用来确认身份。你必须在须要使用 Handoff 的设备上登陆同一个 Apple ID,这样苹果就能够知道哪些设备是你的。这样一来,只有你的硬件才能有权限访问你的数据。
你的 Handoff 只属于你,这也意味着,即便你身边的人也使用苹果设备工做,甚至他们也使用 Handoff,你也永远没必要担忧你的数据会和他们的数据混在一块儿。
2. Handoff 只在你的手够得着的距离生效
当两个或更多个 Apple 设备互相接近在必定的范围内时,设备之间将会创建了一个低功耗蓝牙链接供 Handoff 使用。这一个距离限制可以很是有效地保护你的隐私:由于这一距离很近,近到基本是你肢体可以够得着的地方,这同时也方便了你迅速切换设备工做。
这样一来,你根本没必要担忧发生如下状况:当你在家使用 iPad 浏览网页时,公司里的 Mac 电脑上也会弹出相应内容;或当你在星巴克喝咖啡小憩使用 Mac 电脑时,你家里的 iPad 上不停弹出相应的内容。
3. 可靠的加密密钥,保证 Handoff 安全
当设备之间创建起 Handoff 链接时,它使用苹果推送通知(APN)服务。这意味着,Handoff 使用着至关安全的加密服务,就像 iMessage 那样,Handoff 将会为每个使用 Handoff 的设备分配一个对称的 256 位 AES 密钥,这个密钥直接存储在每一个设备的钥匙串中。苹果公司声称,这一方法极其安全,能够防止外界的恶意攻击。
一旦设备之间链接就绪,Handoff 将使得周围的设备「知晓」源设备上正在进行的活动。举例来讲,若是你正在使用 Safari 浏览网页,Handoff 将让其它设备知道你正在使用 Safari 浏览器;若是你使用 Keynote 编辑幻灯片,Handoff 将让这些设备知道你正在使用 Keynote 软件。
通常说来,低功耗蓝牙的通讯都使用了上述的加密方式来传输数据,并且 APN 也是使用同一种类型的加密类型。
4. 你让它工做,Handoff 才会开始工做
Handoff 在准备就绪后基本上不怎么耗费数据传输——它不会在你的设备间时刻推送你的源设备活动数据,它只是让其它设备都「知晓」你的设备活动能够在它们之上继续。可是 Handoff 的一切,只有你选择开始,它才开始。
在你的 iOS 设备的锁屏上,向上滑动 Handoff 图标,或者调出应用程序切换器后滑动到最左边点击对应的 Handoff 卡片;在 Mac 电脑上,你能够点击 Dock 栏中的 Handoff 图标,或使用 Command-Tab 调出应用程序切换器来选择对应的 Handoff 图标。
瞧,Handoff 为你主导,在任何状况下,只有当你须要切换设备了,它才开始全速工做。
5. Handoff 的数据传输全程加密
一旦触发 Handoff,Handoff 将使用低功耗蓝牙或 APN 加密方式处理这些数据的传输。这个数据能够是你正在浏览的网页地址,能够是你正在编辑的幻灯片网页,能够是你正在编辑的 iCloud 文档。
Handoff 还能够在网页端和应用程序之间传输信息,但在创建链接以前,应用程序必须证实它本身控制着该网站的域名。换句话说,Facebook 的应用程序必须证实它控制着 Facebook.com 域名。
那么若是须要传输大量数据呢?例如你在编写的邮件中附着一个很大的附件,Handoff 首先使用低功耗蓝牙配对,而后走点对点的 Wi-Fi 通道(相似于 AirDrop),这个过程也是全程加密的。
结语
若是你不想使用Handoff,无论是在 iOS 设备上仍是在 Mac 电脑上,你均可以很容易地禁用它。不然,Handoff 将在知足条件下尽一切所能但愿在多设备之间同步你的操做。再次说明,设备的距离限制控制着你的隐私,加密的传输过程保护着你的数据安全,Handoff 至关可靠。
iCloud
iCloud是苹果提供的云端服务,用户能够将通信录、备忘录、邮件、照片、音乐、视频等备份到云服务器并在各个苹果设备间直接进行共享而无需关心数据同步问题,甚至即便你的设备丢失后在一台新的设备上也能够经过Apple ID登陆同步。固然这些内容都是iOS内置的功能,那么对于开放者如何利用iCloud呢?苹果已经将云端存储功能开放给开发者,利用iCloud开发者能够存储两类数据:用户文档和应用数据、应用配置项。前者主要用于一些用户文档、文件的存储,后者更相似于平常开放中的偏好设置,只是这些配置信息会同步到云端。
要进行iCloud开发一样须要一些准备工做(下面的准备工做主要是针对真机的,模拟器省略Provisioning Profile配置过程):
一、2步骤仍然是建立App ID启用iCloud服务、生成对应的配置(Provisioning Profile),这个过程当中Bundle ID可使用通配符(Data Protection、iCloud、Inter-App Audio、Passbook服务在建立App ID时其中的Bundle ID是可使用通配ID的)。
3.在Xcode中建立项目(假设项目名称为“kctest”)并在项目的Capabilities中找到iCloud并打开。这里须要注意的就是因为在此应用中要演示文档存储和首选项存储,所以在Service中勾选“Key-value storae”和“iCloud Documents”:
1、扩展概述
扩展(Extension)是iOS 8中引入的一个很是重要的新特性。扩展让app之间的数据交互成为可能。用户能够在app中使用其余应用提供的功能,而无需离开当前的应用。
在iOS 8系统以前,每个app在物理上都是彼此独立的,app之间不能互访彼此的私有数据。
而在引入扩展以后,其余app能够与扩展进行数据交换。基于安全和性能的考虑,每个扩展运行在一个单独的进程中,它拥有本身的bundle, bundle后缀名是.appex。扩展bundle必须包含在一个普通应用的bundle的内部。
iOS 8系统有6个支持扩展的系统区域,分别是Today、Share、Action、Photo Editing、Storage Provider、Custom keyboard。支持扩展的系统区域也被称为扩展点。
Today Widget
对于赛事比分,股票、天气、快递这类须要实时获取的信息,能够在通知中心的Today视图中建立一个Today扩展实现。Today扩展又称为Widget。
Share
在iOS 8以前,用户只有Facebook,Twitter等有限的几个分享选项能够选择。若是但愿将内容分享到Pinterest,开发者则须要一些额外的努力。在iOS 8中,开发者能够建立自定义的分享选项。
Action
action在全部支持的扩展点中扩展性最强的一个。它能够实现转换另外一个app上下文中的内容。苹果在WWDC大会上演示了一个Bing翻译动做扩展,它能够将在Safari中选中的文本翻译成不一样的语言。
Photo Editing
在iOS 8以前,若是你想为你的照片添加一个特殊的滤镜,你须要进入第三方app中,这个过程是至关繁琐的。在iOS 8中,你能够直接在Photos中使用第三方app,如Instagram,VSCO cam、Aviary提供的Photo Editing扩展完成对图片的编辑,而无需离开当前的app。
Storage Provider
Storage Provider让跨多个文件存储服务之间的管理变得更简单。相似Dropbox、Google Drive等存储提供商经过在iOS 8中提供一个Storage Provider扩展,app直接可使用这些扩展检索和存储文件而再也不须要建立没必要要的拷贝。
Custom Keyboard
苹果公司在2007年率先推出了触摸屏键盘,但一直没多大改进。在这一方面,Android则将键盘权限开放给了第三方开发者,因此出现了许多像Swype,SwiftKey等优秀的键盘输入法。在iOS 8中,苹果终于将键盘权限开发给了第三方开发者,自定义键盘输入法可让用户在整个系统范围内使用。
2、建立扩展与发布扩展
在建立扩展以前,你须要建立一个用来包含扩展的常规的app项目。这个包含扩展的app被称为containing app。在建立好containg app以后,选择File->New->Target菜单,从弹出的对话框中选择一个适当的扩展目标模板。每个扩展目标模板都包含了与扩展点相关的文件和设置。一个containing app能够包含多个不一样类型的扩展。
每个扩展目标模板包含一个头文件和实现文件,一个Info.plist文件,以及一个storyboard文件。Info.plist文件包含了对扩展的配置信息,其中最重要的键是NSExtension。下面列出了一个NSExtension可能包含的经常使用键值对。
NSExtension
NSExtensionAttributes
NSExtensionActivationRule
NSExtensionActivationSupportsImageWithMaxCount
10
NSExtensionActivationSupportsMovieWithMaxCount
1
NSExtensionJavaScriptPreprocessingFile
MyJavaScriptFile
NSExtensionPointVersion
1.0
NSExtensionMainStoryboard
MainInterface
NSExtensionPointIdentifier
com.apple.ui-services
NSExtensionPrincipalClass
ActionViewController
1) NSExtensionActivationRule定义了当前的扩展支持的数据类型及数据项个数,例如当前的设置只支持图片格式和视频格式的数据,而且最多不超过10张图片和1个视频。
2) NSExtensionJavaScriptPreprocessingFile用于配置与脚本交互的JS脚本文件的名字。
3) NSExtensionMainStoryboard配置扩展的Storyboard文件名。
4) NSExtensionPointIdentifier用于表示扩展点,每个扩展点拥有一个惟一的名字。
5) NSExtensionPrincipalClass配置当扩展启动时,扩展点首先要实例化的类
为了将扩展提交苹果商店,你须要提交你的containg app。而且须要注意,除了扩展必须包含功能之外,同时containg app还须要提供一些功能,而针对OS X平台的扩展则无此限制。当用户安装了你的containg app,containg app中包含的扩展也会一同被安装。
3、理解扩展如何运做
在安装扩展以后,扩展并不会自动运行,用户必须执行特定的操做来启用扩展。若是是Today扩展,用户能够在通知中心的Today视图中编辑启用扩展。若是是自定义键盘扩展,用户须要在系统设置的通用选项下的键盘选项中启用自定义键盘扩展。而若是是Share扩展,用户只需点击系统提供的分享按钮,便可在分享列表中找到分享扩展。
一个扩展并非一个app,它的生命周期和运行环境不一样于普通app。在生命周期方面,扩展的生命周期从用户在另外一个app中选择了扩展开始,一直到扩展完成了用户的请求生命周期结束。在运行环境方面,扩展的限制要比普通app更严格,扩展的可用内存上限以及可用的API都比普通app要少。严格限制扩展的内存是由于在同一时间可能会有多个扩展同时运行,如Widget扩展。若是API声明包含NS_EXTENSION_UNAVAILABLE宏,则此API在扩展中将不可用,常见的API如:
+ (UIApplication *)sharedApplication NS_EXTENSION_UNAVAILABLE_IOS(“Use view controller based solutions where appropriate instead.”);
调用扩展的应用称为host app,对于Widget扩展,host app就是Today。host app会在扩展的有效生命周期内定义一个扩展上下文。经过扩展上下文,host app能够和扩展互传数据。注意,扩展只和host app直接通讯,扩展与containg app以及containing app与host app之间不存在通讯关系,若是扩展须要打开containg app,则经过自定义URL scheme方式实现,而不是直接向containg app发送消息。三者的关系见下图:
扩展是一个单独的个体。扩展拥有独立的target,独立的bundle文件,独立的运行进程,独立的地址空间。这意味着即便你的containing app不在运行,系统也能够启动扩展。或者你的containing app处于挂起状态,一样不会影响扩展的运行。因此系统能够单独对扩展执行优化。扩展与containg app的关系:
4、设计扩展过程当中常见的几个问题
1. containg app与扩展如何经过扩展上下文互传数据
在iOS 8中,UIViewController新增了一个扩展上下文属性extensionContext。来处理containing app与扩展之间的通讯,上下文的类型是NSExtensionContext。假设你如今须要在host app中将一张图片传递给扩展作滤镜处理,host app中的代码以下:
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[[self.imageView image]] applicationActivities:nil];
[self presentViewController:activityViewController animated:YES completion:nil];
当用户在弹出的Action列表中选择了扩展,扩展将被启动,而后在扩展的viewDidLoad方法中,经过extensionContext检索host app传回的数据项。扩展中的代码以下:
- (void)viewDidLoad {
[super viewDidLoad];
NSExtensionItem *imageItem = [self.extensionContext.inputItems firstObject];
if(!imageItem){
return;
}
NSItemProvider *imageItemProvider = [[imageItem attachments] firstObject];
if(!imageItemProvider){
return;
}
// 检查是否包含文本
if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]){
[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *image, NSError *error) {
if(image){
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}
}];
}
}
上述代码中,extensionContext表示一个扩展到host app的链接。经过extionContent,你能够访问一个NSExtensionItem的数组,每个NSExtensionItem项表示从host app传回的一个逻辑数据单元。你能够从NSExtensionItem项的attachments属性中得到附件数据,如音频,视频,图片等。每个附件用NSItemProvider实例表示。上述代码中NSItemProvider的loadItemForTypeIdentifier实例方法的第一个参数是(NSString )kUTTypeImage,若是你须要处理的是文本类型,参数则为(NSString )kUTTypeText,相应的处理代码则变成:
if([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeText]){
[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeText options:nil completionHandler:^(NSAttributedString *string, NSError *error) {
if (string) {
// 在这里处理文本
}
}];
}
当扩展处理完host app传回的图片数据后,它须要将处理好的数据再传给host app。在扩展中的代码以下:
-(IBAction)done:(id)sender{
NSExtensionItem* extensionItem = [[NSExtensionItem alloc] init];
[extensionItem setAttachments:@[[[NSItemProvider alloc] initWithItem:[self.imageView image] typeIdentifier:(NSString*)kUTTypeImage]]];
[self.extensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];
}
最后一步是host app接收来自扩展传回的数据,在host app中的代码以下:
[activityViewController setCompletionWithItemsHandler:^(NSString activityType, BOOL completed, NSArray *returnedItems, NSError error){
if([returnedItems count] > 0){
NSExtensionItem* extensionItem = [returnedItems firstObject];
NSItemProvider* imageItemProvider = [[extensionItem attachments] firstObject];
if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]){
[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *item, NSError *error) {
if(item && !error){
dispatch_async(dispatch_get_main_queue(), ^{
[self.imageView setImage:item];
});
}
}];
} } }];
上述代码主要是经过设置一个completionBlock处理数据回调。
注意,全部的扩展都是一个UIViewController。因此UIViewController的全部生命周期方法,如viewWillAppear:、viewWillDisappear:等在扩展中都是可使用的。
2. 如何在扩展中打开containing app
在通常状况下,扩展和containing app不存在通讯关系。可是有时候须要在扩展中打开containing app,如iOS 7中预置的日历Widget。在常规的app中,可使用以下代码在A app中打开B app:
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:customURL]]) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:customURL]];
}
可是以前有讲到,sharedApplication API在扩展中被禁止使用,因此为了实现一样的功能,NSExtensionContext定义了一个新的方法用来打开containing app:
- (void)openURL:(NSURL *)URL completionHandler:(void (^)(BOOL success))completionHandler;
在调用此方法以前,须要在containg app中定义一个自定义URL Scheme。定义方法可参见连接,最终的结果以下图:
在扩展中打开containing app的代码以下:
- (IBAction)openContainingApp:(id)sender {
NSURL *url = [NSURL URLWithString:@”ExtensionDemo://”];
[self.extensionContext openURL:url completionHandler:^(BOOL success) {
}];
}
3. 如何实现containing app与扩展共享数据
扩展和containing app各自拥有本身的数据容器,虽然扩展内嵌在containing app的内部,可是它们并不能够互访彼此的数据。为了实现containing app与扩展的数据共享,苹果在iOS 8中引入了一个新的概念——App Group。为了开启App Group,找到你的containing app目标,在右侧找到Capabilities标签,定位到App Groups分组,以下图所示。
而后选择你须要共享数据的扩展目标,重复执行一次操做,注意两次的App Group名要相同,不要添加新的条目。当开启App Group后,你可使用NSUserDefaults方法访问共享区域,以下述代码,注意不是[NSUserDefaults standardUserDefaults]:
_sharedUserDefault= [[NSUserDefaults alloc] initWithSuiteName:@”group.com.aegeaon.ExtensionDemo”];
你也可使用NSFileManager的containerURLForSecurityApplicationGroupIdentifier方法访问共享数据区:
- (BOOL)saveTextByNSFileManager {
NSError *err = nil;
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@”group.wangzz”];
containerURL = [containerURL URLByAppendingPathComponent:@”Library/Caches/good”];
NSString *value = _textField.text; BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&err]; if (!result) { NSLog(@"%@",err); } else { NSLog(@"save value:%@ success.",value); } return result;
}
App Group区域在containing app与扩展之间所处的关系图:
你可能注意到了,在Xcode 6中iPhone模拟器的位置已经发生了变化。与此同时,在iOS 8 release Note中有提到,app的沙盒结构已经发生了改变,如今它被划分红了三个容器,Bundle容器、Data容器、iCloud容器。iOS 8 app沙盒目录结构以下图:
为了具体了解沙盒目录的布局,使用以下代码分别在containing app和扩展中打印出App Group目录,app bundle目录,以及Document目录:
- (void)logAppPath
{
//app group路径
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@”group.com.aegeaon.ExtensionDemo”];
NSLog(@”app group:\n%@”,containerURL.path);
//打印可执行文件路径 NSLog(@"bundle:\n%@",[[NSBundle mainBundle] bundlePath]); //打印documents NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *path = [paths objectAtIndex:0]; NSLog(@"documents:\n%@",path);
}
在containing app中执行logAppPath方法的结果以下:
app group:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Shared/AppGroup/5B4CFBD8-D95D-4F01-9268-D9F79792147D
bundle:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Bundle/Application/EED1F771-A8AD-4A97-97F3-2B0A57936C17/ExtensionDemo.app
documents:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Data/Application/95DBF43A-8B4B-426C-9A3A-C1745FCB3FA2/Documents
在扩展中执行logAppPath方法的结果以下:
app group:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Shared/AppGroup/5B4CFBD8-D95D-4F01-9268-D9F79792147D
bundle:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31-9509-4076-BC94-BF4D29DC0151/data/Containers/Bundle/Application/EED1F771-A8AD-4A97-97F3-2B0A57936C17/ExtensionDemo.app/PlugIns/ExpressExt.appex
documents:
~/Documents
其中标注为红色的意思是每次运行目录名都会发生变化。标注为绿色的表示文件名不会变化的,标准为橘色也验证了iOS 8中沙盒目录被划分的说法。其中也能够看出扩展扩展名为appex,它包含在containing app的PlugIns目录内。下图展现了扩展目录在Finder中的结构:
MyExtensionJavaScriptClass.prototype = {
run: function(arguments) {
arguments.completionFunction({“baseURI”: document.baseURI});
},
finalize: function(arguments) { var newContent = arguments["content"]; document.write(newContent); }
};
var ExtensionPreprocessingJS = new MyExtensionJavaScriptClass;
其中包含一个run()和finalize()方法。当Safari一加载好你的JS文件,就会当即调用run方法,当你在扩展中调用了completeRequestReturningItems:expirationHandler:completion:方法,Safari会调用finalize()方法。在run()方法中,Safari提供了一个arguments参数,使用它能够利用键值对的形式将数据传给扩展。在上述代码中,传给扩展的键值对是:@{@”baseURI” : document.baseURI}。在finalize()方法中,当你调用了completeRequestReturningItems:expirationHandler:completion:方法,方法第一个参数的值会传给finalize()方法的arguments形参。在上述代码中,finalize()接收到参数后,将内容写入了当前的文档。
为了Safari可以调用正确调用到JS文件,须要在扩展的Info.plist文件中添加以下配置:
NSExtensionAttributes
NSExtensionJavaScriptPreprocessingFile
MyJavaScriptFile
在你的扩展中,为了取得从JS脚本传回的键值对,你须要为NSItemProvider的方法loadItemForTypeIdentifier:options:completionHandler:指定kUTTypePropertyList数据类型,在取得返回的键值字典后,使用NSExtensionJavaScriptPreprocessingResultsKey键取值,代码以下:
NSExtensionContext *context = self.extensionContext;
NSExtensionItem *item = context.inputItems.firstObject;
NSItemProvider *provider = item.attachments.firstObject;
[provider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) { NSDictionary *results = (NSDictionary *)item; NSString *baseURI = [[results objectForKey:NSExtensionJavaScriptPreprocessingResultsKey] objectForKey:@"baseURI"]; NSLog(@"%@", baseURI); }];
为了在扩展中将处理后的结果传给脚本,你须要使用NSItemProvider的initWithItem:typeIdentifier:包装键值对。代码以下:
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = @[[[NSItemProvider alloc] initWithItem: @{NSExtensionJavaScriptFinalizeArgumentKey: @{@”content”:@”Hello World”}} typeIdentifier:(NSString *)kUTTypePropertyList]];
[[self extensionContext] completeRequestReturningItems:@[extensionItem] expirationHandler:nil completion:nil];
5. 如何在containing app与扩展之间共享代码
iOS 8中,你能够内嵌一个framework文件在扩展和containing app之间共享代码。假设你但愿在你的containing app与扩展之间共享图片处理的代码,此时你能够将代码打包成framework文件,内嵌到两个目标中。对于内嵌框架中的代码,确保不包含扩展不容许使用的API。
如何将代码打包成framework文件这里就不敖述了,感兴趣的同窗能够参见:http://blog.sina.com.cn/s/blog_407fb5bc01013v6s.html。当你建立好.framework文件后,你能够直接将.framework文件同时拖入containing app和扩展中,以下图所示:
这里使用公司ILSLib目录下的的MagicalRecord21.framework文件做为素材,讲解如何在containing app和自定义键盘扩展之间实现共享Core Data数据库。在你的扩展和containing app中中配置好引用头文件。分别在containing app的AppDelegate文件的application: didFinishLaunchingWithOptions: launchOptions与自定义键盘扩展的UIInputViewController子类文件中viewDidLoad方法中添加以下代码:
[MagicalRecord setupCoreDataStackWithStoreNamed:@”demo.sqlite”];
上述代码分别对containing app和扩展执行Core Data栈初始化,其中包括数据模型、sqlite存储文件等配置。运行containing app,此时AppDelegate中的数据库配置代码会被执行,接着打开系统设置中的通用选项下的键盘选项,在这里启用自定义键盘。而后回到containing app,切换到自定义键盘扩展,此时自定义键盘扩展中viewDidLoad方法中的数据库配置代码执行,可是控制台出现错误提示:
CoreData: error: -addPersistentStoreWithType:SQLite configuration:(null) URL:~/Library/Application%20Support/CustomKeyboardExt/demo.sqlite – file:/// options:(null) … returned error Error Domain=NSCocoaErrorDomain Code=512 “The operation couldn’t be completed. (Cocoa error 512.)” UserInfo=0x7b48a720 {reason=Failed to create file; code = 2} with userInfo dictionary {
reason = “Failed to create file; code = 2”;
}
上述错误表示在扩展的~/Library/Application%20Support/CustomKeyboardExt/demo.sqlite目录建立.sqlite文件失败。翻阅MagicalRecord源代码(须要从github从新下载源代码,.framework看不到源代码),其中在建立.sqlite存储文件路径的代码中会发现:
+ (NSURL ) MR_urlForStoreName:(NSString )storeFileName {
NSArray *paths = [NSArray arrayWithObjects:[self MR_applicationDocumentsDirectory], [self MR_applicationStorageDirectory], nil];
NSFileManager *fm = [[NSFileManager alloc] init];
for (NSString *path in paths) {
NSString *filepath = [path stringByAppendingPathComponent:storeFileName];
if ([fm fileExistsAtPath:filepath]) {
return [NSURL fileURLWithPath:filepath];
}
}
//set default url return [NSURL fileURLWithPath:[[self MR_applicationStorageDirectory] stringByAppendingPathComponent:storeFileName]];
}
其中MR_applicationStorageDirectory方法返回的是Application Support目录,而这个目录是处在Library目录内的。上文中已经讲过,扩展没有Documents目录,一样也是没有Library目录。因此文件建立会发生失败。为了实现扩展与containing app之间共享.sqlite文件,这里须要将.sqlite文件建立在App Group区域。问题是MagicalRecord21.framework文件只暴露了头文件,没法对其源文件中的MR_urlForStoreName:方法作修改。这里使用Objective-C的动态运行时技术——Method Swizzling,在运行时将MR_urlForStoreName:方法的实现使用新的实现进行替换。 (注:这里能够直接给setupCoreDataStackWithStoreNamed方法传递一个包含文件路径的URL类型参数实现修改.sqlite文件的存放位置,methodSwizzling只是另外一种通用处理方法)
首先须要为自定义键盘扩展建立一个Category文件NSPersistentStore+Tracking.h/m,.m文件中的完整的代码以下:
(NSURL ) ILS_urlForStoreName:(NSString )storeFileName {
NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:kGroupName];
storeURL = [storeURL URLByAppendingPathComponent:[kContainingDirectory stringByAppendingString:storeFileName]];
return storeURL;
}
@end
在当前的代码一载入内存,load方法将被执行,它比AppDelegate的application: didFinishLaunchingWithOptions: launchOptions方法要先被执行,上述代码会将MR_urlForStoreName:的实现替换成ILS_urlForStoreName:,在ILS_urlForStoreName:方法中,使用NSFileManager的containerURLForSecurityApplicationGroupIdentifier方法设定App Group,最终的.sqlite文件将保存在App Group目录内的CoreDataStore目录下。一样须要为containing app中使用此方法,能够直接将NSPersistentStore+Tracking.h/m拖入containing app目标内。再次运行自定义键盘扩展,数据库文件已成功保存到App Group中。以下图:
同时被共享的代码框架MagicalRecord21.framework被containg app和扩展共享,双方共用一个框架文件,以下图:
如何在扩展中处理长时间任务
用户但愿在扩展完成他们的任务以后可以当即返回到host app中。可是若是扩展执行的任务是一个长时间任务,好比下载。在这种状况下,须要使用NSURLSession来建立一个下载session,并初始化一个后台下载任务。当扩展初始化了上传下载任务后,就算是完成了host app的请求,扩展就能够被终止。这不会影响到任务的结果。若是当后台任务完成后,你的扩展不在运行,系统将在后台启动你的contaiing app并调用appdelegate的aplication:handleEventsForBackgroundURLSession:completionHandler:方法。为了在扩展中初始化一个后台的NSURLSession任务,你必须设置一个containing app和扩展均可以访问的共享容器。
相关代码以下:
NSURLSession *mySession = [self configureMySession];
NSURL *url = [NSURL URLWithString:@”http://www.example.com/LargeFile.zip“];
NSURLSessionTask *myTask = [mySession downloadTaskWithURL:url];
[myTask resume];
2.获取上述文件夹的路径
①获取沙盒的路径
NSLog(@"%@",NSHomeDirectory());
② 获取Documents目录路径:
/*返回值是数组的缘由:
该方法一开始用于mac -os开发,对于PC端来讲,能够有多个用户,因此获取时,会获取到全部用户的文件夹路径,
可是该方法用于ios开发时,由于移动端只有一个用户,因此获取到的路径也只有一个. */
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
③ 获取Library目录路径:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES); NSString *libraryDirectory = [paths objectAtIndex:0];
④ 获取Cache目录路径:
NSArray *cacPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES);
NSString *cachePath = [cacPath objectAtIndex:0];
⑤ 获取Tmp目录路径:
NSString *tmpDirectory = NSTemporaryDirectory();
⑥建立文件和文件
//建立文件夹 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); NSString *documentsDirectory = [paths firstObject]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *testDirectory = [documentsDirectory stringByAppendingPathComponent:@"test"]; // 判断是否建立文件成功 BOOL res=[fileManager createDirectoryAtPath:testDirectory withIntermediateDirectories:YES attributes:nil error:nil]; //<span style="font-family:georgia,verdana,Arial,helvetica,sans-seriff;">建立文件</span> NSString *testPath = [testDirectory stringByAppendingPathComponent:@"test.txt"]; BOOL res=[content writeToFile:testPath atomically:YES encoding:
NSUTF8StringEncoding error:nil];
⑦删除文件
NSFileManager *defaultManager = [NSFileManager defaultManager]; if ([defaultManager isDeletableFileAtPath:filePath]) { [defaultManager removeItemAtPath:filePath error:nil]; } //文件是否存在 NSLog(%@, [fileManager isExecutableFileAtPath:filePath]?@"YES":@"NO");
⑧.NSUserDefaults
//</span>程序运行时,判断用户是否登陆 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSString *str = [userDefaults objectForKey:@"username"];
⑨NSBundle
// NSBundle表示 app 中代码和资源的文件在文件系统里所在的位置,app编译后,资源文件直接就复制到了该目录下
//得到程序所在路径
NSString *boundlePath = [[NSBundle mainBundle] bundlePath];
NSLog(@”%@”,boundlePath);
//根据文件名和文件类型找到文件的绝对路径
NSString *file = [[NSBundle mainBundle] pathForResource:name ofType:nil];
3.文件写入和读取操做
①获取文件路径
②字符串的写入和读取操做.
//写入操做
NSString str = @”aad”;
BOOL isSuccess =[str writeToFile:[self filePath] atomically:YES
encoding:NSUTF8StringEncoding error:&error];
if (isSuccess) {
NSLog(@”成功”);
} else {
NSLog(@”失败”);
}
//字符串从文件中读取数据
NSString *text = [NSString stringWithContentsOfFile:[self filePath] encoding:NSUTF8StringEncoding error:nil];
self.tf2.text = text;
③数组的写入和读取操做
//写入 NSArray *dataArr = @[@"aa", @"nn"]; BOOL isSuccess = [dataArr writeToFile:[self filePath] atomically:YES]; //读取 NSArray *arr = [NSArray arrayWithContentsOfFile:[self filePath]];
④字典的写入和读取操做
//写入 NSDictionary *dic = @{@"name":@"zhang",@"hehe":@"dad"}; BOOL isSuccess = [dic writeToFile:[self filePath] atomically:YES];
//读取
NSDictionary *dic =[NSDictionary dictionaryWithContentsOfFile:[self filePath]] ;
⑤NSData写入和读取文件
//将字符串转为data型 NSData *data = [@"dasf" dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; //写入 [data writeToFile:[self filePath] atomically:YES]; //将data型转化为字符串 NSString * newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; //读写 NSData *data1 =[NSData dataWithContentsOfFile:[self filePath]];
4.归档与反 归档
对象归档实例:Encoding
“归档”是值另外一种形式的序列化,对模型对象进行归档的技术能够轻松将复杂的对象写入文件,而后再从中读取它们,只要在类中实现的每一个属性都是基本数据类型(如int或float)或都是符合NSCoding协议的某个类的实例,你就能够对你的对象进行完整归档。
对,对象进行归档,服从协议
NSCoding协议声明了两个方法:(required)
-(void)encodeWithCoder:(NSCoder *)aCoder,是将对象写入到文件中。
-(id)initWithCoder:(NSCoder *)aDecoder,是将文件中数据读入到对象中。
实现这两个协议方法
1).建立Contact对象(Contact.h文件)
@interface Contact : NSObject
//若是要对一个对象进行归档操做,须要让该类服从NSCoding协议
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *gender;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *phone;
2).(Contact.m文件)实现协议
//当对一个对象进行归档时,自动调用该方法,为该对象的实例变量进行归档操做.
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:@”name”];
[aCoder encodeObject:_gender forKey:@”gender”];
[aCoder encodeObject:_phone forKey:@”phone”];
[aCoder encodeObject:@(_age) forKey:@”age”];
}
//当对于一个对象进行反归档时,自动调用该方法,为该对象的实例变量进行反归档.
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.name = [aDecoder decodeObjectForKey:@”name”];
self.gender = [aDecoder decodeObjectForKey:@”gender”];
self.age = [[aDecoder decodeObjectForKey:@”age”] integerValue];
self.phone = [aDecoder decodeObjectForKey:@”phone”];
}
return self;
}
//初始化
- (instancetype)initWithName:(NSString )name gender:(NSString )gender age:(NSInteger)age phone:(NSString *)phone {
self = [super init];
if (self) {
self.name = name;
self.gender = gender;
self.age = age;
self.phone = phone;
}
return self;
}
3).实现归档操做
Contact *contact = [[Contact alloc] initWithName:@”aa” gender:@”man” age:18 phone:@”138”];
NSMutableData *mData = [NSMutableData data];
//1.建立归档工具对象
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mData];
//2.开始归档
[archiver encodeObject:contact forKey:@”lock”];
//3.结束归档
[archiver finishEncoding];
[contact release];
[archiver release];
//4.写入文件
BOOL isSuccess = [mData writeToFile:[self getFilePath] atomically:YES];
if (isSuccess) {
NSLog(@”归档成功”);
} else {
NSLog(@”归档失败”);
}
//另外一种方式
//[NSKeyedArchiver archiveRootObject:contact toFile:[self getFilePath]];
4).反归档操做
//1.从本地读取数据 NSData *data = [NSData dataWithContentsOfFile:[self getFilePath]]; //2.建立反归档工具 NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; //3.开始反归档操做 Contact *contact = [unArchiver decodeObjectForKey:@"lock"]; //结束反归档 [unArchiver finishDecoding]; [unArchiver release];
//另外一种方式:
//Contact *contact = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getFilePath]];
VoiceOver是什么?请举例解释一下iOS中的辅助功能(Accessibility)。开发者如何使用这些功能?
iOS应用是如何实现后台多任务处理(Multitasking)的?
Game Center针对iOS游戏有哪些功能?
iBeacons是什么?
Cocoa/Cocoa Touch是什么?
请归纳一下Core Audio,Core Data以及Core Location各是什么。它们对iOS应用有何意义?
请描述SpriteKit和SceneKit的做用。
Metal是什么?
响应链(Responder Chain)是什么?它是如何发挥做用的?
按钮和其余控制方式对哪些操做作出回应?
AppDelegate扮演着什么样的角色?
请解释一下NSUserDefaults。就你而言,你会如何在磁盘中对数组对象进行序列化?
你会如何储存用户的认证信息?
请问何为Keychain服务?
为何移动设备上的缓存和压缩是不可或缺的?
请解释一下~/Documents,~/Library和~/tmp。 iOS中的~属于什么目录?
AirPlay是如何运行的?换作是你,你会如何经过编程提升应用的实用性以及演示效果?
传感器,IO以及WiFi、拨号等链接方式如何在iOS平台上运做?它们有何利用价值?请扼要地谈谈你的观点。
iPad 2,iPad mini 1-3,iPad Retina,iPad Air 2,iPhone 五、5S、6以及6+在硬件性能方面有何差别?这对注重性能的应用有何限制?
关于编程
Cocoa Touch包含什么?不包含什么?
为何Cocoa Touch的类名称是以两个大写字母开头的?
这是峰驼命名法的时候
Swift和Objective-C分别是什么?二者相比有何不一样之处,又有何联系?
为何Optional在Swift语言中很是重要?
请解释一下NSError。在Swift中,什么状况下能使用NSError ,什么状况下不能?
请说明如何使用Instancetype及其重要性。
在Swift中,何时该用let,何时该用var?
为何map函数必不可少?该在什么状况下使用它?
你会选择什么工具来追踪Bug?
若是在Cocoa中发现一个Bug,你会如何处理?
若是应用的新版本出现了Regression的状况,该如何补救?如何防止用户在使用过程当中遇到新的Bug?
Objective-C的类是怎么执行的?Objective-C Runtime是如何实现的?
iOS是如何提升安全性,保护用户隐私信息的?
应用能够下载并即刻显示数据。如何根据MVC来判断下载的最佳位置?
MVC对代码库(Codebase)的设计有何影响?
Controller Life-Cycle以及View Life-cycle分别有哪些调试方法?
iOS使用的是哪些设计模式(Design Patterns)?你的代码库使用的是哪些设计模式?
iOS提供哪些线程?如何充分利用这些线程?
请简要描述一下UIScrollView的执行过程。它是如何响应手势识别(Gesture Recognizer)、多点触控(Multi-Touch)和Run Loop的?
你认为iOS须要添加或改进哪些API?
关于界面
iPhone五、六、6+以及iPad Air 2的屏幕分辨率分别是多少?
分辨率的计算单位是什么?
请解释一下Interface Builder的做用以及NIB文件的概念。
iOS UI的图像储存类型是什么?
请描述一下Storyboard和标准NIB文件的差异。
设备状态栏(Device Status Bar)是什么?高度如何?是否透明?在手机通话或者导航状态下,它是如何显示的?
导航栏(Navigation Bar)是什么?可否拿出你的iPhone,指出你下载的哪些应用运用了导航栏?
选项卡(Tab Bar)和工具栏(Toolbar)分别是什么?二者之间有何共同点和不一样点?
表视图(Table View)是什么?集合视图(Collection View)又是什么?
何时用“弹出(Popover)”属性最为合适?
Split-view Controller是什么?
选取器视图(Picker View)适合存放哪类内容?
应该在什么状况下使用标签、文本域和文本视图?
分段控件(Segmented Control)的做用是什么?
模态视图(Modal View)是什么?
iOS通知属于什么类型?
关于设计
iOS应用图标是指什么?请尽量详细地描述一下。
最小尺寸和最大尺寸的应用图标分别是什么样子的?
应用图标可否包含透明的部分?
Newsstand的图标与常规应用有何不一样?
请解释一下启动画面(Launch Images)。
自动布局(Auto Layout)的做用是什么?请归纳一下它是如何运行的。
设计软件时为何要加上动画?
请描述一下软件设计中的交互和Feedback有什么做用。
设计iPhone和iPad应用时,应分别考虑哪些因素?
请描述一下原型设计对于软件开发的意义。其做用是什么?
关于App Store
应用内购买(In-App Purchases)是怎么回事?IAP可以为用户带来哪些新体验?
你是否在App Store上发布过应用?可否归纳一下过程?
iTunes Connect是什么?
Provisioning Profiles是指?
App ID是什么?
iOS的开发和发布签名证书有何异同?
如何使用TestFlight?经过Ad-hoc发布应用的话,该如何使用UUID?
应什么时候验证购买收据?
发布iAds(苹果平台广告)有哪些要求?
趣味问答
最近有没有开发什么好玩的东西?你最引觉得豪的做品是什么?
谈一谈你经常使用的开发工具都有哪些优点?
你最敬佩的独立Mac或者iOS应用开发者是谁?
最喜欢什么项目?哪一种类型的?
你以为Xcode有哪些须要改进的地方?
iOS上你最喜欢哪些API?
是否有最中意的错误报告?
你最爱以哪一种方式来检验一项新技术是否好用?
为何词典被称做Dictionaries,而不是HashTable或HashMap?