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

相关推荐
镭封16 小时前
短剧多人对白怎么配?一人完成多角色配音的办法
人工智能·音视频·语音识别·媒体
技灵AI18 小时前
Wan 3.0 API怎么做多参考商品视频?从图片、视频、音频分工到30秒交付
人工智能·prompt·aigc·音视频·wan 3.0
赛博仓鼠19 小时前
MiniMax H3 实测:8GB显存跑2K视频+原生音频,开源视频生成的新卷王
开源·aigc·音视频
byte轻骑兵20 小时前
【LE Audio】PBP精讲[4]: 公共广播通告的设计逻辑与数据交互流程
人工智能·音视频·le audio·低功耗蓝牙音频
hz5678920 小时前
手术室视频示教系统解决方案:让临床教学突破空间限制
安全·音视频·实时音视频·信息与通信·智能硬件
可乐鸡翅yeah_1 天前
混合内容 Mixed‑Content 安全策略,HLS HTTPS 页面加载 HTTP 资源排错实战
运维·ffmpeg·音视频·媒体·m3u8
揽秀亭长1 天前
音轨分离处理方案及效果测试分析
人工智能·音视频
美狐美颜SDK开放平台1 天前
直播APP如何兼顾画质与低延迟?视频美颜SDK性能优化开发思路
人工智能·深度学习·音视频·美颜sdk·第三方美颜sdk
终端安全笔记1 天前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios
大熊猫侯佩1 天前
独立 App 首发完成 iPhone Duo 展开态适配
ios·swiftui·swift