iOS sdk中为我们提供了一套完善的文字排版开发组件
:CoreText。CoreText库中提供了很多的工具来对文本进行操作,例如CTFont、CTLine、CTFrame等。利用这些工具可以对文字字体每一行每一段落进行操作。
此例中默认图片都在右上方,且为了美观和开发简便设定所占宽度都相同。
1. 首先,需要引入CoreText库
在需要使用的类文件中添加#import 头文件。
2. 设置文本的参数
创建一个NSMutableAttributedString对象,包含所需展示的文本字符串。这样就可以对其进行设置了。通过CTFontCreateWithName函数创建一个CTFont对象,利用NSMutableAttributedString对象的addAttribute方法进行设置。类似的方法可以设置字间距。
对其方式与行间距的设置方式:
[cpp] view plain
copy
?
(alignment);
同样使用addAttribute设置字符串对象。这样的方法还可以设置行间距,段间距等参数。
3. 计算图片所占高度。图片可以使用UIImageView 来进行显示。很容易便可获取每张图片所占总高度。
4. 由于图片宽度是固定的这样就可以计算每行文字缩短的字数。只要文本的总体高度低于图像总高度则文字长度都是缩短的。用CTTypesetterSuggestLineBreak函数动态的计算每一行里的字数,因为每一行里面的中文字、标点符号、数字、字母都不一样所以可以显示的字数肯定也是不同的,所以需要作这样的计算。这样循环直至文本结束,就可以知道有多少行字了。再根据字体高度和行间距得出总的文本高度,如果文本高度大于图片总高度那么显示区域的Frame高度就是文本的高度,反之亦然。
5. 绘制文本:
设置每一行绘制文本的区间:
[cpp]
view plaincopyprint?
6. 其他功能:
在完成文本绘制功能后可以加入调整文字大小的功能,和图片的放大的功能。
文字大小可以通过直接设置字体大小后重新绘制文本来实现。
图片放大可以在视图上添加一个新的UIImageView 来展示放大后的图片,并且加入动画效
如何发送本地推送通知
推送通知也属于UI的一部分,所以推送通知对象是以UI开头。
将发送通知的代码方法控制器的-touchesBegan: withEvent: 中测试比较合适,如果放到viewDidLoad方法,用户的注册请求还没有完成方法就调用了。
创建本地通知对象
1
2
// 创建本地通知对象
UILocalNotification*ln=[[UILocalNotification alloc]init];
设置本地通知属性
// 1.设置通知的内容(如果此属性不设置是不会发送通知的)
ln.alertBody=@"小明,你妈叫你回家吃饭了!";
// 2.设置通知触发的开始时间
ln.fireDate=[NSDate dateWithTimeIntervalSinceNow:3];
// 3.设置重复通知的时间,间隔
ln.repeatInterval=NSCalendarUnitSecond;
// 4.设置重复执行使用日历(用户设置的日历)
ln.repeatCalendar=[NSCalendar currentCalendar];
// NSString * const NSGregorianCalendar; 公历
// NSString * const NSChineseCalendar; 农历
// ln.repeatCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSChineseCalendar];
// 5.设置应用图标右上角的数字
ln.applicationIconBadgeNumber=3;
// 6.设置点击推送通知进入界面的时候显示,加载图片
ln.alertLaunchImage=@"";
// 7 设置通知的音效(只有真机有效)
local.soundName=UILocalNotificationDefaultSoundName;
// 8 设置一些额外信息
local.userInfo=@{@"QQ":@"55555",@"info":@"约了没"};
// iOS8.0 以后新增属性
// ************************************
// 1.设置区域,进入或离开某个区域的时候触发
// CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(40.1,106.1);
// ln.region = [[CLCircularRegion alloc] initWithCenter:coordinate radius:10.0 identifier:@"ab"];
// 2.设置进入或离开某个区域只执行一次
// ln.regionTriggersOnce = YES;
// ***************************************
// iOS8.2 新增属性
// ln.alertTitle = @"通知标题";
使用应用 UIApplication 调度本地通知
1
2
// 让应用调度通知
[[UIApplication sharedApplication]scheduleLocalNotification:ln];
无论应用是在前台,后台还是已经关闭都能如期接收到本地通知,但是当用户点击通知进入应用的时候,我们需要根据不同情况,进行处理
AppDelegate本地通知代理方法
-(void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification{
// 当应用在前台时候什么都不做
if(application.applicationState==UIApplicationStateActive){
return;
}
// 当应用不再前台的时候才去跳转,这样用户体检更好
UITabBarController*tbVc=(UITabBarController*)application.keyWindow.rootViewController;
tbVc.selectedIndex=1;
}
但是当应用已经退出的时候,点击通知进入本应用时候,不在调用application:didReceiveLocalNotification:的代理方法。难道当应用退出后,用户再进入应用我们就不再跳转指定界面了吗?
为了更好用户体验,我此时也应该让应用跳转到指定的界面,怎么才能实现这个功能呢?
我们知道当应用程序启动的时候一定会调用application: didFinishLaunchingWithOptions:的代理方法,在这里我们能拿到本地通知信息,也可以跳转相应的界面。
// 如果是点击本地通知进来的那么launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]就会有内容
if(launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]){
//页面跳转
UITabBarController*tbVc=(UITabBarController*)self.window.rootViewController;
tbVc.selectedIndex=1;
}
一个应用可能要各种不同的通but知,点击不同的通知可以跳转不同界面,这个有该怎么做呢?
在发送通知时候,设置userInfo属性
1
2
// 7.设置应用信息
ln.userInfo=@{@"pageKey":@"friend"};
在AppDelegate本地通知代理方法中进行判断
* 一旦接收到本地通知就会调用该方法
* 注意这个方法:应用在前台也会调用
* @param application 应用
* @param notification 本地通知对象
*/
-(void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification{
// 当应用在前台时候什么都不做
if(application.applicationState==UIApplicationStateActive){
return;
}
// 当应用不再前台的时候才去跳转,这样用户体检更好
// 获取tabBarController
UITabBarController*tbVc=(UITabBarController*)self.window.rootViewController;
// 获取用户设置的跳转页
NSString*page=notification.userInfo[@"pageKey"];
// 如果是朋友圈
if([page isEqualToString:@"session"]){
tbVc.selectedIndex=1;
}else{
// 否则跳转到好友
tbVc.selectedIndex=0;
}
}
测试launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]中的内容
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions{
// Override point for customization after application launch.
// 获取UIApplicationLaunchOptionsLocalNotificationKey对应内容
id obj=launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
// 获取控制器(注意此时需要通过self.window,通过application.keyWindow无法获取到,因为此时的window还没有成为keyWindow)
UITabBarController*tbVc=(UITabBarController*)self.window.rootViewController;
// 获取索引为0的控制(注意此时tbVc.selectedViewController为nil)
UIViewController*vc=tbVc.viewControllers[0];
// 创建一个文本
UILabel*label=[[UILabel alloc]init];
label.backgroundColor=[UIColor brownColor];
// 设置text为UIApplicationLaunchOptionsLocalNotificationKey对应的内容
label.text= [obj description];
label.frame=CGRectMake(10,100,300,400);
label.numberOfLines=0;
// 添加到控制器的View上
[vc.viewaddSubview:label];
returnYES;
}
显示结果:
我们从中可以出他是一个UILocalNotification对象
所以我们取出UILocalNotification对象,剩下的做法与接收到本地通知代理方法中处理相同,所以我们把它提取为一个公用的方法
-(void)jumpToPageWithLocalNotification:(UILocalNotification*)notification{
// 获取tabBarController
UITabBarController*tbVc=(UITabBarController*)self.window.rootViewController;
// 获取用户设置的跳转页
NSString*page=notification.userInfo[@"pageKey"];
// 如果是朋友圈
if([page isEqualToString:@"session"]){
tbVc.selectedIndex=0;
}else{
// 否则跳转到好友
tbVc.selectedIndex=1;
}
}
在didReceiveLocalNotification方法中
-(void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification{
// 当应用在前台时候什么都不做
if(application.applicationState==UIApplicationStateActive){
return;
}
// 当应用不再前台的时候才去跳转,这样用户体检更好
[selfjumpToPageWithLocalNotification:notification];
}
在didFinishLaunchingWithOptions方法中
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions{
// Override point for customization after application launch.
// 如果是点击本地通知进来的那么launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]就会有内容
UILocalNotification*notifcation=launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
// 如果存在通知
if(notifcation){
[selfjumpToPageWithLocalNotification:notifcation];
}
returnYES;
}
你如果把上面的程序运行在iOS8上,会爆出如下错误
1
Attemptingtoschedulealocalnotification{fire date=Monday,July13,2015at9:02:25AM ChinaStandard Time,time zone=(null),repeat interval=0,repeatcount=UILocalNotificationInfiniteRepeatCount,next fire date=Monday,July13,2015at9:02:25AMChina Standard Time,user info={pageKey=friend;}}with an alert but haven'treceived permissionfrom the user todisplay alerts
也就是在iOS8上要发送本地通知需要 请求用户权限 如何请求用户权限呢?
一般在新版有变化的地方,在头文件中都会有相应的说明,所以点击到scheduleLocalNotification:方法中,看看有没有我们需要信息。
1
2
// 让应用调度通知
[[UIApplication sharedApplication]scheduleLocalNotification:ln];
1
// In iOS 8.0 and later, your application must register for user notifications using -[UIApplication registerUserNotificationSettings:] before being able to schedule and present UILocalNotifications
意思就是说:在iOS8.0以后,在调度通知之前你需要使用UIApplication的对象方法registerUseNotificationSetting:来请求用户授权。
这种请求权限的代码一般放在didFinishLaunchingWithOptions:方法中,在用户不卸载的情况下,只需要请求一次,下次在运行就不用请求了!
// 1.如果是iOS8请求用户权限
if ([UIDevice currentDevice].systemVersion.doubleValue >= 8.0) {
// 向用户请求通知权限
// categories暂时传入nil
UIUserNotificationSettings*setting=[UIUserNotificationSettingssettingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil];
[application registerUserNotificationSettings:setting];
}
运行程序
测试点击通知,进入应用也没问题。
接下来,我们说说-[UIUserNotificationSettings settingsForTypes:categories:] 中的 categories
categories可以让我们发送通知之前预定义一些通知也就是通知上可以显示按钮,他需要是一个装有UIUserNotificationCategory类的对象的NSSet的对象.。但是官方推荐我们使用它的子类UIMutableUserNotificationCategory,来动态的添加通知的行为按钮,iOS8支持前台和后台的两种行为。
通知Action按钮以长条展示如图
通知Action按钮以AlertView展示如图
注册分类,并在分类中添加不同的行为 由于注册用户通知设置代码量比较大我们实现一个新的方法registerUserNotification
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
- (void)registerUserNotification {
// 向用户请求通知权限
// 1.设置用户通知权限类型
UIUserNotificationTypetypes=UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
// 2.创建通知的行为按钮
// 2.1创建第一个行为
UIMutableUserNotificationAction*action1=[[UIMutableUserNotificationAction alloc]init];
// 2.1.1 设置行为的唯一标示
action1.identifier=UIMutableUserNotificationActionBackground;
// 2.1.2 设置通知按钮的的标题
action1.title=@"后台";
// 以什么样模式运行应用
// UIUserNotificationActivationModeForeground, // 当应用在前台的时候触发
// UIUserNotificationActivationModeBackground // 即使应用不在前台也触发
action1.activationMode=UIUserNotificationActivationModeBackground;
// 2.1.3 是否只有锁屏的锁屏状态下才能显示
action1.authenticationRequired=NO;
// 2.1.4 按钮的性质
action1.destructive=NO;
// 2.1创建第一个行为
UIMutableUserNotificationAction*action2=[[UIMutableUserNotificationAction alloc]init];
// 2.1.1 设置行为的唯一标示
action2.identifier=UIMutableUserNotificationActionForeground;
// 2.1.2 设置通知按钮的的标题
action2.title=@"前台";
// 以什么样模式运行应用
// UIUserNotificationActivationModeForeground, // 当应用在前台的时候触发
// UIUserNotificationActivationModeBackground // 即使应用不在前台也触发
action2.activationMode=UIUserNotificationActivationModeForeground;
// 2.1.3 用户必须输入密码才能执行
action2.authenticationRequired=YES;
// 2.1.4 按钮的性质(没有效果)
action2.destructive=YES;
// 3.创建用户通知分类
UIMutableUserNotificationCategory*category=[[UIMutableUserNotificationCategory alloc] init];
// 3.1 设置类别的唯一标识
category.identifier=@"myCategory";
// 3.2 设置通知的按钮
// Context:
// UIUserNotificationActionContextDefault, //默认上下文(情景)下的英文(通常都是)
// UIUserNotificationActionContextMinimal //通知内容区域受限情况下内容
[category setActions:@[action1,action2]forContext:UIUserNotificationActionContextDefault];
// 4.创建用户通知的设置信息
UIUserNotificationSettings*setting=[UIUserNotificationSettings settingsForTypes:typescategories:[NSSet setWithObject:category]];
// 注册设置
[[UIApplication sharedApplication]registerUserNotificationSettings:setting];
}
在发送本地推送通知时候指定通知的分类标示
1
2
// 9.设置通知的类别
ln.category=@"myCategory";
监听点击通知按钮的行为,在AppDelegate中实现监听通知按钮点击方法
* 当用户点击通知上定制的按钮执行的行为(注意:不点击行为按钮,不会进入该方法)
*
* @param application 应用
* @param identifier 行为标识符
* @param notification 本地通知
* @param completionHandler 完成回调
*/
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
// 处理不同行为
if ([identifier isEqualToString:UIMutableUserNotificationActionBackground]) {
NSLog(@"后台运行程序");
}else if ([identifier isEqualToString:UIMutableUserNotificationActionForeground]) {
NSLog(@"前台运行程序");
}else{
NSLog(@"其他");
}
// 在当任务完成的时候,调用任务完成的block
completionHandler();
}
基于位置的本地通知就是当用户进入或离开某个区域,就会给该用户发送一条本地通知,基于位置的本地通知只有在iOS8.0以后才能使用。
实现步骤:
在- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions 方法中注册通知权限
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions{
// Override point for customization after application launch.
if([application respondsToSelector:@selector(registerUserNotificationSettings:)]){
UIUserNotificationTypetypes=UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound;
UIUserNotificationSettings*setting=[UIUserNotificationSettings settingsForTypes:typescategories:nil];
[application registerUserNotificationSettings:setting];
}
returnYES;
}
在发送通知之前要请求使用用户的位置权限,在info.plist增加 NSLocationWhenInUseUsageDescription 的Key,类型为String,值填写需要提示内容。
定一个 CLLocationManager 类型的属性
1
@property(nonatomic,strong)CLLocationManager*locManager;
通过懒加载的方式其进行初始化并设置其代理
// 懒加载位置管理器对象
-(CLLocationManager*)locManager{
if(_locManager==nil){
_locManager=[[CLLocationManager alloc]init];
_locManager.delegate=self;
}
return_locManager;
}
发送基于位置的推送通知之前首先判断用户是否已经拥有了访问位置的权限。如果如果没有请求权限,如果已经有就直接发送推送通知。
-(IBAction)baseLocationNotification{
// 请求注册权限
if([CLLocationManager authorizationStatus]!=kCLAuthorizationStatusAuthorizedWhenInUse){
//请求使用用户位置权限
[self.locManagerrequestWhenInUseAuthorization];
}else{
// 发送基于位置本地通知
[selfscheduleBaseLocationNotification];
}
}
// 监听用户授权的改变
-(void)locationManager:(CLLocationManager*)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
if(status==kCLAuthorizationStatusAuthorizedWhenInUse){
// 发送基于位置通知
[selfscheduleBaseLocationNotification];
}
}
实现发送基于位置的通知方法
-(void)scheduleBaseLocationNotification{
// 创建本地通知对象
UILocalNotification*loc=[[UILocalNotification alloc]init];
// 设置通知提示内容
loc.alertBody=@"欢迎来到传智博客";
// 创建经纬度坐标
CLLocationCoordinate2Dcoordinate=CLLocationCoordinate2DMake(23.139009,113.363798);
// 设置触发的区域,当用户进入或离开这个区域的时候就会触发
loc.region=[[CLCircularRegion alloc]initWithCenter:coordinateradius:100identifier:@"loc"];
// 设置是否只触发一次
// YES 表示只触发一次(用户进入或离开这个区域的时候触发一次) NO 表示可以(用户每次进入或离开这个区域的时候都会触发)
loc.regionTriggersOnce=NO;
// 调度通知
[[UIApplication sharedApplication]scheduleLocalNotification:loc];
}
说明:
1.基于位置本地通知,提醒做不到非常精确他依赖与GPS和周围的信号基站。
2.基于位置的本地通知,只有真机才能测试。
3.基于位置的本地通知,无需编程人员主动获取用户位置,我们添加应用调度中,如果用户手机打开了定位,系统会根据用户当前的位置来决定是否要给该用户发送通知。
写博客第一百一天;
QQ:565803433