在iOS通知中显示富媒体内容可以显著提升用户体验。通过UNNotificationAttachment
,我们可以为本地和远程通知添加图片、音频、视频等内容。
基本实现方法
1. 创建带附件的通知
swift
func scheduleNotificationWithImage() {
// 1. 创建通知内容
let content = UNMutableNotificationContent()
content.title = "图片通知"
content.body = "这是一条带图片的推送通知"
content.sound = UNNotificationSound.default
// 2. 准备图片附件
guard let imageURL = Bundle.main.url(forResource: "notification_image", withExtension: "jpg"),
let attachment = try? UNNotificationAttachment(
identifier: "imageAttachment",
url: imageURL,
options: nil
) else {
print("无法创建图片附件")
return
}
content.attachments = [attachment]
// 3. 设置触发器(5秒后)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// 4. 创建请求
let request = UNNotificationRequest(
identifier: "imageNotification",
content: content,
trigger: trigger
)
// 5. 添加到通知中心
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("添加图片通知失败: \(error.localizedDescription)")
} else {
print("图片通知已安排")
}
}
}
2. 支持的文件类型
iOS通知附件支持以下类型:
文件类型 | 扩展名 | 限制 |
---|---|---|
图片 | .jpg, .png, .gif | 最大10MB |
音频 | .mp3, .aiff, .wav | 最大5MB |
视频 | .mp4, .mov | 最大50MB |
其他 | 见官方文档 | 需符合UTI规范 |
高级用法
1. 动态下载网络图片
swift
func scheduleNotificationWithRemoteImage(imageURLString: String) {
// 1. 下载图片
guard let url = URL(string: imageURLString) else { return }
URLSession.shared.downloadTask(with: url) { tempURL, response, error in
guard let tempURL = tempURL, error == nil else {
print("图片下载失败: \(error?.localizedDescription ?? "未知错误")")
return
}
// 2. 保存到临时目录
let fileManager = FileManager.default
let filename = url.lastPathComponent
let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory())
let localURL = tempDirectory.appendingPathComponent(filename)
do {
// 移除已存在的文件
if fileManager.fileExists(atPath: localURL.path) {
try fileManager.removeItem(at: localURL)
}
// 移动文件
try fileManager.moveItem(at: tempURL, to: localURL)
// 3. 创建附件
let attachment = try UNNotificationAttachment(
identifier: "remoteImage",
url: localURL,
options: [UNNotificationAttachmentOptionsTypeHintKey: kUTTypeJPEG]
)
// 4. 创建通知
let content = UNMutableNotificationContent()
content.title = "网络图片通知"
content.body = "下载的图片已附加到通知"
content.attachments = [attachment]
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(
identifier: "remoteImageNotification",
content: content,
trigger: trigger
)
// 5. 添加通知
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("添加远程图片通知失败: \(error.localizedDescription)")
}
}
} catch {
print("创建附件失败: \(error.localizedDescription)")
}
}.resume()
}
2. 视频附件处理
swift
func scheduleNotificationWithVideo() {
// 1. 获取视频URL(可以是本地或下载后的远程视频)
guard let videoURL = Bundle.main.url(forResource: "demo", withExtension: "mp4") else {
print("找不到视频文件")
return
}
// 2. 创建视频附件
do {
let attachment = try UNNotificationAttachment(
identifier: "videoAttachment",
url: videoURL,
options: [UNNotificationAttachmentOptionsTypeHintKey: kUTTypeMPEG4]
)
// 3. 创建通知内容
let content = UNMutableNotificationContent()
content.title = "视频通知"
content.body = "点击查看视频内容"
content.attachments = [attachment]
// 4. 设置触发器
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// 5. 创建并发送通知
let request = UNNotificationRequest(
identifier: "videoNotification",
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("添加视频通知失败: \(error.localizedDescription)")
}
}
} catch {
print("创建视频附件失败: \(error.localizedDescription)")
}
}
附件选项配置
1. 常用选项
swift
let options: [AnyHashable: Any] = [
// 指定文件类型
UNNotificationAttachmentOptionsTypeHintKey: kUTTypePNG,
// 是否隐藏缩略图(仅音频)
UNNotificationAttachmentOptionsThumbnailHiddenKey: false,
// 缩略图裁剪区域(CGRect字典)
UNNotificationAttachmentOptionsThumbnailClippingRectKey: [
"X": 0.25,
"Y": 0.25,
"Width": 0.5,
"Height": 0.5
],
// 缩略图时间点(视频,秒)
UNNotificationAttachmentOptionsThumbnailTimeKey: 5
]
let attachment = try UNNotificationAttachment(
identifier: "customImage",
url: imageURL,
options: options
)
2. 自适应图片大小
swift
func resizeImageForNotification(originalURL: URL, targetSize: CGSize) -> URL? {
guard let image = UIImage(contentsOfFile: originalURL.path) else { return nil }
let renderer = UIGraphicsImageRenderer(size: targetSize)
let resizedImage = renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: targetSize))
}
let tempDir = URL(fileURLWithPath: NSTemporaryDirectory())
let resizedURL = tempDir.appendingPathComponent("resized_\(originalURL.lastPathComponent)")
do {
try resizedImage.pngData()?.write(to: resizedURL)
return resizedURL
} catch {
print("保存调整大小后的图片失败: \(error)")
return nil
}
}
最佳实践与注意事项
1. 文件大小优化
swift
func optimizeImageForNotification(originalURL: URL) -> URL? {
guard let imageData = try? Data(contentsOf: originalURL),
let image = UIImage(data: imageData) else {
return nil
}
// 1. 检查文件大小
let fileSize = imageData.count / 1024 // KB
print("原始文件大小: \(fileSize)KB")
// 2. 如果超过限制则压缩
if fileSize > 1024 { // 假设限制为1MB
let compressionQuality: CGFloat = 1024.0 / CGFloat(fileSize)
if let compressedData = image.jpegData(compressionQuality: compressionQuality) {
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent("optimized_\(originalURL.lastPathComponent)")
try? compressedData.write(to: tempURL)
return tempURL
}
}
return originalURL
}
2. 清理临时文件
swift
func cleanTempAttachments() {
let fileManager = FileManager.default
let tempDir = URL(fileURLWithPath: NSTemporaryDirectory())
do {
let tempFiles = try fileManager.contentsOfDirectory(at: tempDir, includingPropertiesForKeys: nil)
for file in tempFiles {
// 删除所有临时附件文件
if file.lastPathComponent.hasPrefix("optimized_") ||
file.lastPathComponent.hasPrefix("resized_") {
try fileManager.removeItem(at: file)
}
}
} catch {
print("清理临时文件失败: \(error)")
}
}
3. 错误处理与回退
swift
func safeAddAttachment(to content: UNMutableNotificationContent, imageURL: URL) {
do {
let optimizedURL = optimizeImageForNotification(originalURL: imageURL) ?? imageURL
let attachment = try UNNotificationAttachment(
identifier: "fallbackImage",
url: optimizedURL,
options: nil
)
content.attachments = [attachment]
} catch {
print("添加附件失败,使用纯文本通知: \(error)")
// 回退方案:使用文本通知
content.body += " [包含图片,加载失败]"
}
}
实际应用示例
1. 社交媒体通知
swift
func sendPostNotification(userName: String, postText: String, imageURL: URL?) {
let content = UNMutableNotificationContent()
content.title = "\(userName) 发布了新内容"
content.body = postText
content.sound = .default
content.categoryIdentifier = "POST_NOTIFICATION"
// 如果有图片则添加
if let imageURL = imageURL {
safeAddAttachment(to: content, imageURL: imageURL)
}
// 添加用户交互按钮
let likeAction = UNNotificationAction(
identifier: "like",
title: "👍 喜欢",
options: []
)
let commentAction = UNTextInputNotificationAction(
identifier: "comment",
title: "💬 评论",
options: [.foreground],
textInputButtonTitle: "发送",
textInputPlaceholder: "说点什么..."
)
let category = UNNotificationCategory(
identifier: "POST_NOTIFICATION",
actions: [likeAction, commentAction],
intentIdentifiers: [],
options: []
)
UNUserNotificationCenter.current().setNotificationCategories([category])
// 立即触发通知
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: nil // 立即触发
)
UNUserNotificationCenter.current().add(request)
}
2. 电商促销通知
swift
func sendPromotionNotification(productName: String, discount: String, imageURL: URL) {
let content = UNMutableNotificationContent()
content.title = "限时特惠"
content.body = "\(productName) 直降 \(discount)"
content.sound = .default
content.categoryIdentifier = "PROMOTION_NOTIFICATION"
// 添加产品图片
safeAddAttachment(to: content, imageURL: imageURL)
// 添加交互按钮
let buyNowAction = UNNotificationAction(
identifier: "buyNow",
title: "立即购买",
options: [.foreground]
)
let addToCartAction = UNNotificationAction(
identifier: "addToCart",
title: "加入购物车",
options: []
)
let category = UNNotificationCategory(
identifier: "PROMOTION_NOTIFICATION",
actions: [buyNowAction, addToCartAction],
intentIdentifiers: [],
options: []
)
UNUserNotificationCenter.current().setNotificationCategories([category])
// 设置明天上午10点的触发器
var dateComponents = DateComponents()
dateComponents.hour = 10
let trigger = UNCalendarNotificationTrigger(
dateMatching: dateComponents,
repeats: false
)
let request = UNNotificationRequest(
identifier: "promo_\(productName)",
content: content,
trigger: trigger
)
UNUserNotificationCenter.current().add(request)
}
通过以上方法,可以为iOS通知添加丰富的媒体内容,显著提升用户体验和通知的点击率。记得在实际使用时考虑性能优化和错误处理,确保在各种情况下都能提供良好的用户体验。