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

相关推荐
友善的猴子6 小时前
Adobe Photoshop 2025 Mac中文 Ps图像编辑
macos·adobe·photoshop
伊织code7 小时前
macOS Chrome - 打开开发者工具,设置 Local storage
chrome·macos·设置·开发者工具·local storage
独隅8 小时前
在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级配置的详细步骤
网络·macos
慕雪华年9 小时前
【SLAM】将realsense-viewer录制的rosbag视频导出成图片序列(RealSense D435)
音视频
feiyangqingyun11 小时前
用纯Qt实现GB28181协议/实时视频/云台控制/预置位/录像回放和下载/事件订阅/语音对讲
qt·音视频·gb28181·qt监控国标
追风林11 小时前
mac 解压 nsz 文件
macos
Antonio91511 小时前
【音视频】SDL渲染YUV格式像素
c++·音视频
_多拉不懂A梦11 小时前
基于ImGui+FFmpeg实现播放器
c++·ffmpeg·音视频
18538162800余。14 小时前
碰一碰发视频源码搭建,定制化开发,OEM贴牌
音视频
超周到的程序员15 小时前
大模型开发:源码分析 Qwen 2.5-VL 视频抽帧模块(附加FFmpeg 性能对比测试)
ffmpeg·音视频