iOS8本地通知(详细分析)

文件一:ViewController.m

/*

 iOS8的本地通知:
     1.本地通知的类型:
         Alert or Banner:通知可以用alert或者banner来显示,这取决于用户在设置中得选择。他们都应当包含通知的消息(当然是可以本地化的)。
         声音(Sound):当一个通知被送达时,你可以‘告诉’iOS播放一段自定义或者系统默认的声音。因为用户不会一直看着设备,被展示的通知有可能会被忽略,所以声音显得很有用。但是,对于不重要的通知,声音不应该被使用。
         Badge:当通知到达时,一个badge数字会在App的图标上显示。当一个通知到达时,badge数字必增加1,当通知被处理后badge数字减1。当badge数字不为0或者为0,iOS会显示或者隐藏badge。
 
         提示:在iOS7时,用户只能点击通知(或者在锁屏时滑动)然后关联的App会启动,最后处理相关动作。现在开发者可以用户提供具体的预先定义的动作了。 相关联的App可以在不被启动的情况下,处理不关键或者重要的任务了,而且也可以根据用户的选择来执行不同的代码。
 
     2.本地通知实现步骤:
         1>规定通知类型
             UIUserNotificationSettings *newNotificationSettings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:categoriesForSettings];
         2>创建通知动作(Notification Actions)
             一个动作就是一个UIMutableUserNotificationAction类的对象。UIMutableUserNotificationAction是iOS8新引入的类,有着许多有用的配置属性:
             
             标示符(identifier):字符串,标示了一个对于整个App唯一的字符串。很明显,你永远不应该在同一个App中定义两个同样地标示符。通过此标示符,我们可以决定在用户点击不同的通知时,调用哪个动作。
             标题(title):用来在展示给用户的动作按钮上。可以是简单地或者本地化的字符串。为了让用户能马上理解动作的含义,一定要仔细考虑这个标题的值,最好是1到2个字符。
             (destructive):布尔值。当设置为true时,通知中相应地按钮的背景色会变成红色。这只会在banner通知中出现。通常,当动作代表着删除、移除或者其他关键的动作是都会被标记为destructive以获得用户的注意。
             authenticationRequired:布尔值。当设置为true时,用户在点击动作之前必须确认自己的身份。当一个动作十分关键时这非常有用,因为为认证的操作有可能会破坏App的数据。
             ActivationMode:枚举。决定App在通知动作点击后是应该被启动还是不被启动。此枚举有两个值: (a)UIUserNotificationActivationModeForeground, (b)UIUserNotificationActivationModeBackground。在background中,App被给予了几秒中来运行 代码。
         3>注册通知设置
             指定通知类型和类目 (category)
             - (void)registerU  serNotificationSettings:(UIUserNotificationSettings *)notificationSettings NS_AVAILABLE_IOS(8_0);
         4>安排本地通知
             UILocalNotification对象的重要属性:
                 fireDate:一个通知应当被显示的日期和时间。NSDate对象。
                 alertBody:通知的内容。应当尽量的简洁明了,这样用户才能马上理解它。
                 alertAction:在默认情况下,点击一个banner通知会导致App启动。在以alert形式显示的通知中,会创建一个和这个动作对应 的按钮。在此属性中,你必须指定这个按钮的标题。比如,在这个示例App中,我们将View List设置为它的标题或者alert的动作
         5>处理通知动作
             #pragma mark - 处理当App在运行时通知如何被处理
             - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification NS_AVAILABLE_IOS(4_0)
             {
                 NSLog(@"Received Local Notification:%@",notification.alertBody);
             }
             #pragma mark - 处理动作:在处理本地通知时,这个代理方法非常 重要,在这里你通过用户的点击执行相应地代码
             - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandler NS_AVAILABLE_IOS(8_0)
             {
                 if([identifier  isEqual: @"editList"])
                 {
                     [[NSNotificationCenter defaultCenter] postNotificationName:@"modifyListNotification" object:nil];
                 }
                 else if([identifier isEqualToString:@"trashAction"])
                 {
                     [[NSNotificationCenter defaultCenter] postNotificationName:@"deleteListNotification" object:nil];
                 }
                 //根据规定我们必须调用它,这样系统才能知道我们已经处理完了通知动作
                 completionHandler();
             }
 */
#import "ViewController.h"

@interface ViewController ()<UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *myTextField;
@property (weak, nonatomic) IBOutlet UITableView *myTableView;
@property (weak, nonatomic) IBOutlet UIButton *myButton;
@property (weak, nonatomic) IBOutlet UIDatePicker *myDatePicker;

@end

@implementation ViewController
{
    NSMutableArray *shoppintList;
}
- (void)viewDidLoad {
    //视图点击,隐藏键盘
    [super viewDidLoad];
    UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] init];
    [tapGR addTarget:self action:@selector(didTap:)];
    [self.view addGestureRecognizer:tapGR];
    
    self.myTableView.dataSource = self;
    self.myTableView.delegate = self;
    self.myTextField.delegate = self;
    self.myDatePicker.hidden = YES;
    
    [self loadShoppingList];
    
    [self setupNotificationSettings];
    
    //接收通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleModifyListNotification) name:@"modifyListNotification" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeleteListNotification) name:@"deleteListNotification" object:nil];
}
#pragma mark - 收到通知执行的方法
- (void)handleModifyListNotification
{
    [self.myTextField becomeFirstResponder];
}
- (void)handleDeleteListNotification
{
    [shoppintList removeAllObjects];
    [self saveShoppingList];
    [self.myTableView reloadData];
}
/*
 一个动作就是一个UIMutableUserNotificationAction类的对象。UIMutableUserNotificationAction是iOS8新引入的类,有着许多有用的配置属性:
 
     标示符(identifier):字符串,标示了一个对于整个App唯一的字符串。很明显,你永远不应该在同一个App中定义两个同样地标示符。通过此标示符,我们可以决定在用户点击不同的通知时,调用哪个动作。
     标题(title):用来在展示给用户的动作按钮上。可以是简单地或者本地化的字符串。为了让用户能马上理解动作的含义,一定要仔细考虑这个标题的值,最好是1到2个字符。
     (destructive):布尔值。当设置为true时,通知中相应地按钮的背景色会变成红色。这只会在banner通知中出现。通常,当动作代表着删除、移除或者其他关键的动作是都会被标记为destructive以获得用户的注意。
     authenticationRequired:布尔值。当设置为true时,用户在点击动作之前必须确认自己的身份。当一个动作十分关键时这非常有用,因为为认证的操作有可能会破坏App的数据。
     ActivationMode:枚举。决定App在通知动作点击后是应该被启动还是不被启动。此枚举有两个值: (a)UIUserNotificationActivationModeForeground, (b)UIUserNotificationActivationModeBackground。在background中,App被给予了几秒中来运行 代码。
*/
- (void)setupNotificationSettings
{
    //这个方法会在viewDidLoad方法中 被调用,这意味着每当App被启动的时候它都会执行一次。很显然一遍又一遍的设置同样地值是在做无用功,这样如果我们将上面的方法用一个if判断执行一下 的话就好了。在这个判断中,我们检查通知的类型是否已经被设定了,如果没有if块中的代码就会被执行。
    //通过下面的代码我们避免了重复注册通知类型。但是如果你想修改通知类型、添加动作或者类目的话,你可以将if开始和结束行注释掉,然后运行一次App。新的设定会被添加,你就能测试一下他们了。然后移除注释,避免重复注册。
    UIUserNotificationSettings *notificationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
    if (notificationSettings == UIUserNotificationTypeNone) {
        UIUserNotificationType notificationTypes = UIUserNotificationTypeAlert|UIUserNotificationTypeSound;
        
        UIMutableUserNotificationAction *justInformAction = [[UIMutableUserNotificationAction alloc] init];
        justInformAction.identifier = @"justInform";
        justInformAction.title = @"OK, got it";
        [justInformAction setActivationMode:UIUserNotificationActivationModeBackground];
        justInformAction.destructive = NO;
        justInformAction.authenticationRequired = NO;
        
        UIMutableUserNotificationAction *modifyListAction = [[UIMutableUserNotificationAction alloc] init];
        modifyListAction.identifier = @"editList";
        modifyListAction.title = @"Edit List";
        modifyListAction.activationMode = UIUserNotificationActivationModeForeground;
        modifyListAction.destructive = NO;
        modifyListAction.authenticationRequired = YES;
        
        UIMutableUserNotificationAction *trashAction = [[UIMutableUserNotificationAction alloc] init];
        trashAction.identifier = @"trashAction";
        trashAction.title = @"Delete List";
        trashAction.activationMode = UIUserNotificationActivationModeBackground;
        trashAction.destructive = YES;
        trashAction.authenticationRequired = YES;
        
        NSArray *actionsArr = [NSArray arrayWithObjects:justInformAction, modifyListAction, trashAction, nil];
        NSArray *actionsArrMinimal = [NSArray arrayWithObjects:trashAction, modifyListAction, nil];
        
        //创建一个新的类目(category)吧,首先我们设置它的标示符(identifier),然后将上面的2个数组分别设置:
        UIMutableUserNotificationCategory *shoppingListReminderCategory = [[UIMutableUserNotificationCategory alloc] init];
        shoppingListReminderCategory.identifier = @"shoppingListReminderCategory";
        [shoppingListReminderCategory setActions:actionsArr forContext:UIUserNotificationActionContextDefault];//默认上下文
        [shoppingListReminderCategory setActions:actionsArrMinimal forContext:UIUserNotificationActionContextMinimal];//空间是有限的
        
        NSSet *categoriesForSettings = [NSSet setWithObjects:shoppingListReminderCategory, nil];
        //第一个参数是我们为通知设置的类型,第二个方法是一个集合(NSSet),在这个集合中必须包含一个App所有通知支持的类目。在本例中,我们只有一个类目,但是我们还是需要使用集合来传递它。
        UIUserNotificationSettings *newNotificationSettings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:categoriesForSettings];
        
        //注册通知
        [[UIApplication sharedApplication] registerUserNotificationSettings:newNotificationSettings];
    }
}
#pragma mark - 安排通知
- (void)scheduleLocalNotification
{
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.fireDate = [self fixNotificationDate:self.myDatePicker.date];
    localNotification.alertBody = @"Hey, you must go shopping, remember?";
    localNotification.alertAction = @"View List";
    
    //指定用户点击通知后对应的类目动作
    //前面已经定义了一个类目和类目标示符,在这里就能使用到这个标示符了:
    [localNotification setCategory:@"shoppingListReminderCategory"];
    
    //需要使用UIApplication的scheduleLocalNotification(_:) 方法来真正的安排一个通知,不然这个通知永远都不会“通知”到你
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
- (NSDate *)fixNotificationDate:(NSDate *)dateToFix
{
    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitDay |NSCalendarUnitMonth |NSCalendarUnitYear |NSCalendarUnitHour |NSCalendarUnitMinute fromDate:dateToFix];
    dateComponents.second = 0;
    NSDate *fixedDate = [[NSCalendar currentCalendar] dateFromComponents:dateComponents];
    return fixedDate;
}
#pragma mark - 点击界面隐藏键盘
- (IBAction)didTap:(UITapGestureRecognizer *)sender {
    if (shoppintList == nil) {
        shoppintList = [[NSMutableArray alloc] init];
    }
    if (self.myTextField.text.length != 0) {
        [shoppintList addObject:self.myTextField.text];
        [self.myTableView reloadData];
        self.myTextField.text = @"";
        [self saveShoppingList];
    }
    
    [self.view endEditing:YES];//收起键盘
}
#pragma mark - 按钮点击事件
- (IBAction)didButtonClicked:(id)sender {
    if (self.myDatePicker.hidden) {
        [self animateMyViews:self.myTableView viewToShow:self.myDatePicker];
        //除所有已经安排的通知
        [[UIApplication sharedApplication] cancelAllLocalNotifications];
    }
    else
    {
        [self animateMyViews:self.myDatePicker viewToShow:self.myTableView];
        [self scheduleLocalNotification];
    }
    self.myTextField.enabled = YES;
}
#pragma mark - tableview和date picker之间动画的切换
- (void)animateMyViews:(UIView *)hideView viewToShow:(UIView *)showView
{
    [UIView animateWithDuration:0.35 animations:^{
        hideView.transform = CGAffineTransformScale(hideView.transform, 0.0001, 0.0001);
    } completion:^(BOOL finished) {
        showView.hidden = NO;
        hideView.hidden = YES;
        
        showView.transform = CGAffineTransformScale(showView.transform, 0.0001, 0.0001);
        [UIView animateWithDuration:0.35 animations:^{
            showView.transform = CGAffineTransformIdentity;
        }];
    }];
}
- (void)removeAtIndex:(NSInteger)index
{
    [shoppintList removeObjectAtIndex:index];
    [self.myTableView reloadData];
//    NSLog(@"shoppintList:%@",shoppintList);
    [self saveShoppingList];
}
#pragma mark - 保存数据
- (void)saveShoppingList
{
    NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
    NSString *pathStr = [pathArr firstObject];
    NSString *savePath = [pathStr stringByAppendingString:@"shopping_list.txt"];
    [shoppintList writeToFile:savePath atomically:YES];
}
#pragma mark - 读取数据
- (void)loadShoppingList
{
    NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
    NSString *pathStr = [pathArr firstObject];
    NSString *savePath = [pathStr stringByAppendingString:@"shopping_list.txt"];
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:savePath]) {
        shoppintList = [NSMutableArray arrayWithContentsOfFile:savePath];
        [self.myTableView reloadData];
    }
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    //返回一个BOOL值,指明是否允许在按下回车键时结束编辑
    //如果允许要调用resignFirstResponder 方法,这回导致结束编辑,而键盘会被收起
    if (shoppintList == nil) {
        shoppintList = [[NSMutableArray alloc] init];
    }
    if (self.myTextField.text.length != 0) {
        [shoppintList addObject:self.myTextField.text];
        [self.myTableView reloadData];
        self.myTextField.text = @"";
        [self.myTextField resignFirstResponder];
        [self saveShoppingList];
    }
    return YES;
}

#pragma mark - UITableViewDataSource和UITableViewDeleGate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
{
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (shoppintList.count != 0) {
        return shoppintList.count;
    }
    else
    {
        return 0;
    }
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 50.0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.myTableView dequeueReusableCellWithIdentifier:@"myCell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] init];
    }
    cell.textLabel.text = [shoppintList objectAtIndex:indexPath.row];
    return cell;
}
//现在让我们在左滑cell后显示的删除按钮上调用上面的方法。要实现此功能,我们需要实现 tableView(tableView:commitEditingStyle:forRowAtIndexPath:) 代理。在此代理方法中我们调用上面的删除方法。
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    //这里有两件事情需要注意一下:1.这个if判断语句是非常必要的,有了这个if判断,删除方法就只会在用户点击删除按钮后被触发。2.代理方法中的indexPath.row的row就是我们想要删除的物品的数组下标。
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [self removeAtIndex:indexPath.row];
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end


文件二:AppDelegate.m

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return YES;
}

#pragma mark - 这个代理方法在程序启动时被调用(不管是正常启动还是通过一个本地通知),包含了所有App通知的设置选项。
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings NS_AVAILABLE_IOS(8_0)
{
    NSLog(@"type:%u",notificationSettings.types);
}
#pragma mark - 处理当App在运行时通知如何被处理
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification NS_AVAILABLE_IOS(4_0)
{
    NSLog(@"Received Local Notification:%@",notification.alertBody);
}
#pragma mark - 处理动作:在处理本地通知时,这个代理方法非常 重要,在这里你通过用户的点击执行相应地代码
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandler NS_AVAILABLE_IOS(8_0)
{
    if([identifier  isEqual: @"editList"])
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"modifyListNotification" object:nil];
    }
    else if([identifier isEqualToString:@"trashAction"])
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"deleteListNotification" object:nil];
    }
    //根据规定我们必须调用它,这样系统才能知道我们已经处理完了通知动作
    completionHandler();
}
- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值