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

相关推荐
向哆哆21 分钟前
YOLOv8 实战指南:如何实现视频区域内的目标统计与计数
yolo·音视频·yolov8
大熊猫侯佩1 小时前
趣谈 TextField 视图在 SwiftUI 各个版本中的进化(上)
swiftui·swift·apple
SSH_55232 小时前
MacOs 安装局域网 gitlab 记录
elasticsearch·macos·gitlab
安和昂3 小时前
【iOS】 GCD小结
macos·ios·cocoa
YourReference3 小时前
iOS 集成网易云信的音视频呼叫组件
ios·音视频通话·网易云信·云信·呼叫组件
Akamai中国3 小时前
使用 Akamai 分布式云与 CDN 保障视频供稿传输安全
分布式·安全·云计算·音视频
Digitally4 小时前
如何轻松将 iPhone 备份到外部硬盘
ios·iphone
Swift社区10 小时前
面试高频图论题『墙与门』:Swift BFS 解法全流程拆解
面试·swift·宽度优先