7.3 在通知中显示图片或视频(UNNotificationAttachment)

在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通知添加丰富的媒体内容,显著提升用户体验和通知的点击率。记得在实际使用时考虑性能优化和错误处理,确保在各种情况下都能提供良好的用户体验。

相关推荐
xiaopengbc42 分钟前
免费的视频音频文档文件选装百种格式转换软件(附下载)
音视频
Shang1809893572643 分钟前
MS2107高性能USB 2.0视频信号和音频采集,支持NTSC/PAL制式,适用于低成本视频采集设备
嵌入式硬件·fpga开发·音视频·硬件工程·信息与通信·dsp开发
老贾专利烩1 小时前
音频共享耳机专利拆解:碰击惯性数据监测与阈值减速识别机制研究
音视频·信息与通信·科技前沿·创新专利
web前端进阶者2 小时前
音视频开发远端未发布视频占位图
音视频·webrtc
syso_稻草人2 小时前
基于 ComfyUI + Wan2.2 animate实现 AI 视频人物换衣:完整工作流解析与资源整合(附一键包)
人工智能·音视频
追风20195 小时前
OSS存储的视频,安卓和PC端浏览器打开正常,苹果端打开不播放,什么原因?
音视频
救救孩子把6 小时前
从 Sora 到 Sora 2:文本生成视频进入下一个阶段(附sora教程)
音视频·sora
2501_915106327 小时前
HTTPS 爬虫实战指南 从握手原理到反爬应对与流量抓包分析
爬虫·网络协议·ios·小程序·https·uni-app·iphone
2501_916007477 小时前
iOS 上架技术支持全流程解析,从签名配置到使用 开心上架 的实战经验分享
android·macos·ios·小程序·uni-app·cocoa·iphone
QMY5205207 小时前
深度优先遍历策略
macos·jupyter·postman