本地推送通知

Wed 13 September 2017 by Little Captain

配置本地推送通知

Step 1

  • 创建和配置包含通知具体内容的 UNMutableNotificationContent 对象
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:@"Wake up!" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:@"Rise and shine! It's morning time!"
        arguments:nil];

Step 2

  • 创建一个 UNCalendarNotificationTrigger, UNTimeIntervalNotificationTrigger, 或 UNLocationNotificationTrigger 对象来描述通知被递送的条件
NSDateComponents* date = [[NSDateComponents alloc] init];
date.hour = 7;
date.minute = 0;
UNCalendarNotificationTrigger* trigger = [UNCalendarNotificationTrigger
       triggerWithDateMatchingComponents:date repeats:NO];

Step 3

  • 通过上面创建两个对象, 再创建一个 UNNotificationRequest 对象
UNNotificationRequest* request = [UNNotificationRequest
       requestWithIdentifier:@"MorningAlarm" content:content trigger:trigger];

Step 4

  • 调用 addNotificationRequest:withCompletionHandler: 方法派送通知.

分配自定义操作

UNNotificationContent *content = [[UNNotificationContent alloc] init];
// 配置内容

// 分配类别, 为内容分配类别, 这样当显示这些内容的时候, 系统就会将这个类别中的操作分配给这个通知
// 在派送通知请求之前, 为此属性分配一个值, 才有效。
content.categoryIdentifier = @"TIMER_EXPIRED";

// 创建请求和派送通知

为通知内容添加声音

  • 为 UNMutableNotificationContent 对象的 sound 属性分配一个 UNNotificationSound 对象
  • UNNotificationSound 对象可以创建自定义声音或默认通知声音, 自定义的音频必须位于 App 的主 bundle 或沙盒的 Library/Sounds 目录中
  • 分配自定义声音时, 仅仅通过文件名分配. 如果系统找到了这个文件就播放, 如果没有就播放默认的音频
// 默认
content.sound = [UNNotificationSound defaultSound];
// 自定义
content.sound = [UNNotificationSound soundNamed:@"MySound.aiff"];

本地通知的派送

  • 创建 UNNotificationRequest 对象, 然后调用 UNUserNotificationCenter 对象的 addNotificationRequest:withCompletionHandler: 方法
  • 系统异步派送本地通知. 当派送完成或失败, 就会执行回调 block
// 创建通知请求对象
UNNotificationRequest* request = [UNNotificationRequest
       requestWithIdentifier:@"MorningAlarm" content:content trigger:trigger];

UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
   if (error != nil) {
       NSLog(@"%@", error.localizedDescription);
   }
}];
  • 计划派送的本地通知将保持活动状态, 直到被系统不计划或被你取消. 当通知被派送, 系统自动将其置为不计划, 除非这是个重复触发的通知.
  • 取消通知, 可以调用 UNUserNotificationCenter 对象的 removePendingNotificationRequestsWithIdentifiers: 方法
  • 一个能够被取消的通知, 必须被分配一个 identifier 给对应的 UNNotificationRequest 对象
  • 调用 removeAllPendingNotificationRequests 方法取消所有被挂起的通知, 它不需要任何 identifier

响应通知事件

  • 为了实现响应, 必须为 UNUserNotificationCenter 对象实现代理. 代理对象必须遵守 UNUserNotificationCenterDelegate 协议. 这个协议明确了, 通知中心如果将通知信息传递给你的 App. 如果你的通知包含自定义操作, 代理是必须的.

App 在前台时, 处理通知

  • App 在前台, 通知到达时, 你可以不派送给通知中心或让系统继续下一步操作(显示在通知中心)
  • 默认情况下, 当 App 在前台时, 通知不会被呈现在通知中心, 而是直接递送通知数据给你的 App
  • 如果要显示这类通知, 需要实现 UNUserNotificationCenter 对象代理的 userNotificationCenter:willPresentNotification:withCompletionHandler: 方法.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
        willPresentNotification:(UNNotification *)notification
        withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
   // 这儿更新你的 App 界面

    // 播放音频, 如果什么也不指定, 系统将静默掉这个通知
   completionHandler(UNNotificationPresentationOptionSound);
}
  • 如果 App 在后台或者没有运行, 系统不会调用 userNotificationCenter:willPresentNotification:withCompletionHandler: 方法. 这时, 系统根据通知自带信息通知用户
  • 你可以通过 UNUserNotificationCenter 对象的 getDeliveredNotificationsWithCompletionHandler: 方法确定是否派送通知

响应用户自定义操作

  • 用户的操作打包在 UNNotificationResponse 对象中
  • 在用户选中你的自定义操作时, 递送给你 App 的 UNUserNotificationCenter 对象的代理
  • 为了接收响应, 代理必须实现 userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: 方法. 这个方法的实现中必须能够处理所有自定义操作, 包括你的 App 和 App 扩展中的所有自定义消息
  • 如果一个响应发出, 而你的 App 和 App 扩展没有运行, 系统会在后台启动你的 App 和 App 扩展来处理这个响应. 使用这个后台时间来更新你的 App 的数据结构和界面显示, 不要做其他无关操作
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
           didReceiveNotificationResponse:(UNNotificationResponse *)response
           withCompletionHandler:(void (^)(void))completionHandler {
           // 匹配通知类别标识符
    if ([response.notification.request.content.categoryIdentifier isEqualToString:@"TIMER_EXPIRED"]) {
        // 匹配不同操作
        if ([response.actionIdentifier isEqualToString:@"SNOOZE_ACTION"])
        {
            // 具体操作
        }
        else if ([response.actionIdentifier isEqualToString:@"STOP_ACTION"])
        {
            // 具体操作
        }

    }

    // 其他通知类别的操作的匹配处理
}

处理系统标准操作

  • 触发系统操作时, 通知中心将调用代理的 userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: 方法
  • 在上述方法中的 response 对象可能包含以下两个标识符之一:
    • UNNotificationDismissActionIdentifier : 表示用户移除了通知, 而没有选中任何自定义操作
    • UNNotificationDefaultActionIdentifier : 表示用户启动了 App, 而没有选中任何自定义操作
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
          didReceiveNotificationResponse:(UNNotificationResponse *)response
          withCompletionHandler:(void (^)(void))completionHandler {
   if ([response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
       // 用户移除了通知, 而没有做任何其他动作
   }
   else if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {
       // 用户启动了 App
   }

   // 处理其他操作
}

App 的通知支持

Wed 13 September 2017 by Little Captain

请求用户授权

UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
   completionHandler:^(BOOL granted, NSError * _Nullable error) {
      // 通过授权结果完成进一步操作
}];
  • 获取准确的用户授权信息使用 UNUserNotificationCenter 对象的 getNotificationSettingsWithCompletionHandler: 方法

配置类别和可操作的通知

针对 App 注册通知类别

UNNotificationCategory* generalCategory = [UNNotificationCategory
     categoryWithIdentifier:@"GENERAL"
     actions:@[]
     intentIdentifiers:@[]
     options:UNNotificationCategoryOptionCustomDismissAction];

// 注册通知类别
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center setNotificationCategories:[NSSet setWithObjects:generalCategory, nil]];

将自定义操作添加到类别中

UNNotificationCategory* generalCategory = [UNNotificationCategory
      categoryWithIdentifier …
read more

ipa 包中的 PNG 文件的正常化

Thu 29 June 2017 by Little Captain

问题描述

  • 图片拖入 iOS 工程后, Xcode 会对其进行特殊处理, 导致图片能在 Apple 相关系统上正常显示, 其他系统都不能正常显示.
  • 之所以能在 Apple 相关系统上正常显示是因为苹果的底层方法实现就做了相应处理.

问题解决

  • 通过苹果的方法把这些特殊图片读入系统, 然后重新保存为 PNG 图片即可.

将这段代码加入你的 Mac 控制台工程, 生成一个可执行程序(如 ipaPNGnormalizer), 在命令行中使用即可, 第一个参数为需要正常化处理的 PNG 文件夹路径. 处理后会在这个目录下创建一个 decode 文件夹, 这个文件夹下存放所有的正常化后的 PNG 图片

#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>

#define NSLog(FORMAT …
read more

Objective-C 代码规范

Tue 13 June 2017 by Little Captain

排版

  • 程序块要采用缩进风格编写,缩进的空格数为4个。对齐只使用空格键,不使用TAB键。
  • 相对独立的程序块之间、变量说明之后必须加空行。
  • 较长的语句要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。
  • 不允许把多个短语句写在一行中,即一行只写一条语句。
  • 大括号添加规则(if、for、函数、方法 ...)
if (...) {

    // 代码开始
}
  • 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、 之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(++、&、! ...),后不应加空格。
  • 一行程序以小于80字符为宜,不要写得过长。
  • block 调用规范
!_block ? : _block(...);
  • 方法声明规则: 一个参数一行
- (NSURLSessionDataTask *)upload:(NSString *)URLString
                      parameters:(nullable id)parameters
                            name:(NSString *)name
                        fileName …
read more

iOS 数据的存储方式

Fri 12 May 2017 by Little Captain

沙盒(磁盘)

沙盒简介

  • 每个iOS应用程序都有自己的应用沙盒
  • 应用沙盒就是文件系统目录, 与其它文件系统隔离
  • 应用必须待在自己的沙盒里, 其它应用不能访问该沙盒

沙盒的访问

  • NSHomeDirectory() : 沙盒的根目录
  • Documents
    • 保存应用运行时生成的需要持久化的数据
    • iTunes同步设备时会备份该目录
    • 不允许放缓存大数据
  • Library/Caches
    • 保存应用运行时生成的需要持久化的数据
    • iTunes同步设备时不会备份该目录
    • 一般存储体积大、不需要备份的缓存数据
  • Library/Preference
    • 保存应用的所有偏好设置
    • iOS的Settings(设置)应用会在该目录中查找应用的设置信息
    • iTunes同步设备时会备份该目录
  • tmp
    • 保存应用运行时所需的临时数据
    • 使用完毕后再将相应的文件从该目录删除
    • 应用没有运行时,系统也可能会清除该目录下的文件
    • iTunes同步设备时不会备份该目录

应用程序包

  • 包含了所有的资源文件和可执行文件
  • [NSBundle mainBundle]
    • 获取应用程序包路径
    • 代表 App 本身
  • 注意和沙盒做区分

偏好设置

  • 小数据
  • 保存用户名、密码(MD5)、是否自动登录、字体大小等设置 …
read more

iOS 文件上传

Thu 11 May 2017 by Little Captain

步骤

设置请求头

[request setValue:@"multipart/form-data; boundary=分割线" forHTTPHeaderField:@"Content-Type"];

设置请求体

  • 非文件参数
--分割线\r\n
Content-Disposition: form-data; name="参数名"\r\n
\r\n
参数值
\r\n
  • 文件参数
--分割线\r\n
Content-Disposition: form-data; name="参数名"; filename="文件名"\r\n
Content-Type: 文件的MIMEType\r\n
\r\n
文件数据
\r\n
  • 结束的标记
--分割线--\r …
read more

iOS 监测网络状态

Thu 11 May 2017 by Little Captain

为什么要监听

  • 让用户了解自己的网络状态,防止一些误会, 怪应用无能
  • 根据用户的网络状态进行智能处理,节省用户流量,提高用户体验
    • WIFI\3G网络:自动下载高清图片
    • 低速网络:只下载缩略图
    • 没有网络:只显示离线的缓存数据

监测网络状态

  • 在网络应用中,需要对用户设备的网络状态进行实时监控
  • 苹果官方提供了一个叫Reachability的示例程序,便于开发者检测网络状态

地址

  • 使用步骤
    • 添加框架SystemConfiguration.framework
    • 添加源代码(Reachability.h 和 Reachability.m)
    • 包含头文件(Reachability.h)
  • 实际开发中一般都使用AFNetworking监测
read more

iOS 网络-HTTP

Thu 11 May 2017 by Little Captain

基本概念

如何找到服务器?

  • 客户端通过URL找到想要连接的服务器

URL

  • Uniform Resource Locator : 统一资源定位符
  • 通过1个URL,能找到互联网上唯一的1个资源
  • 互联网上的每个资源都有一个唯一的URL
  • URL就是资源的地址、位置

URL的基本格式

  • 协议://主机地址/路径
  • 协议 : 不同的协议,代表着不同的资源查找方式、资源传输方式
  • 主机地址 : 存放资源的主机(服务器)的IP地址(域名)
  • 路径 : 资源在主机(服务器)中的具体位置

URL中常见的协议

HTTP

  • 超文本传输协议
  • 访问的是远程的网络资源
  • http://
  • http协议是在网络开发中最常用的协议

file

  • 访问的是本地计算机上的资源
  • file://
  • 不用加主机地址

mailto

  • 访问的是电子邮件地址
  • mailto:

FTP

  • 访问的是共享主机的文件资源
  • ftp://

TCP/IP协议簇

  • 通常意义上,我们使用的网络是在TCP …
read more

iOS 文件下载

Thu 11 May 2017 by Little Captain

小文件

  • 利用NSURLConnection发送一个HTTP请求下载
  • 直接用NSData的 +dataWithContentsOfURL
  • 下载图片, 可以利用SDWebImage框架

大文件

  • 建议使用 NSURLSession 或者第三方框架
read more

iOS 解压缩

Thu 11 May 2017 by Little Captain

使用第三方框架

框架地址

  • ZipArchive
  • 需要引入libz.dylib框架

创建压缩文件

  • createZipFileAtPath:withFilesAtPaths:
  • createZipFileAtPath:withContentsOfDirectory:

解压

  • unzipFileAtPath:toDestination:
read more