使用 Sparkle 实现 macOS 应用自定义更新弹窗

简介

源码 源码1

Sparkle 是 macOS 平台上一个优秀的自动更新框架。虽然 Sparkle 提供了默认的更新界面,但有时我们需要自定义更新弹窗以匹配应用的设计风格。本文将介绍如何使用 Sparkle 实现自定义更新界面。

实现步骤

1. 创建自定义更新窗口

首先需要创建一个继承自 NSPanel 的自定义窗口类:

swift 复制代码
class UpdateNotificationWindow: NSPanel {
    private let stackView = NSStackView()
    private let titleLabel = NSTextField()
    private let messageLabel = NSTextField()
    private let progressBar = NSProgressIndicator()
    private var buttons: [NSButton] = []
    
    init() {
        super.init(contentRect: NSRect(x: 0, y: 0, width: 320, height: 240),
                  styleMask: [.nonactivatingPanel, .titled, .fullSizeContentView],
                  backing: .buffered,
                  defer: false)
        
        setupWindow()
        setupUI()
    }
}

2. 实现自定义用户驱动

需要创建一个实现 SPUUserDriver 协议的类来处理更新流程中的各个状态:

swift 复制代码
final class CustomUserDriver: NSObject, SPUUserDriver {
    private var notificationWindow: UpdateNotificationWindow?
    
    // 检查更新
    func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) {
        ensureNotificationWindow().showChecking {
            cancellation()
        }
    }
    
    // 找到更新
    func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice {
        return await withCheckedContinuation { continuation in
            DispatchQueue.main.async {
                self.ensureNotificationWindow().showUpdateFound { updateChoice, type in
                    continuation.resume(returning: updateChoice)
                }
            }
        }
    }
    
    // 显示下载进度
    func showDownloadDidReceiveData(ofLength length: UInt64) {
        let progress = Double(currentProgress) / Double(downloadSize) * 100
        ensureNotificationWindow().showDownloadProgress(progress)
    }
}

3. 配置更新控制器

创建一个更新控制器类来管理 Sparkle 更新器:

swift 复制代码
final class UpdateController {
    static let shared = UpdateController()
    let updater: SPUUpdater
    private let userDriver: CustomUserDriver
    
    private init() {
        userDriver = CustomUserDriver()
        updater = SPUUpdater(hostBundle: Bundle.main,
                           applicationBundle: Bundle.main,
                           userDriver: userDriver,
                           delegate: nil)
        
        try? updater.start()
        updater.automaticallyChecksForUpdates = true
        updater.updateCheckInterval = 3600 * 6
        updater.setFeedURL(URL(string: "https://your-update-url/appcast.xml"))
    }
}

主要功能

1. 检查更新界面

  • 显示检查更新状态
  • 提供取消按钮

2. 发现更新界面

  • 显示新版本信息
  • 提供"立即安装"和"下次启动时安装"选项

3. 下载进度界面

  • 显示实时下载进度
  • 进度条可视化展示

4. 安装准备界面

  • 提示用户重启应用进行安装
  • 显示安装确认按钮

界面美化

为了让更新窗口更美观,我们可以:

  1. 添加圆角和阴影:
swift 复制代码
contentView?.layer?.cornerRadius = 12
hasShadow = true
  1. 自定义按钮样式:
swift 复制代码
private func createStyledButton(title: String, isPrimary: Bool = false) -> NSButton {
    let button = NSButton(title: title, target: nil, action: nil)
    button.layer?.cornerRadius = 6
    
    if isPrimary {
        button.contentTintColor = .white
        button.layer?.backgroundColor = NSColor.controlAccentColor.cgColor
    } else {
        button.contentTintColor = .black
        button.layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor
    }
    
    return button
}

总结

通过自定义 Sparkle 的更新界面,我们可以:

  1. 提供与应用一致的设计风格
  2. 更好地控制更新流程
  3. 提供更好的用户体验

完整的实现代码可以参考上面的示例。通过这种方式,我们可以为 macOS 应用提供一个既美观又实用的自动更新体验。

注意事项

  1. 确保正确处理所有更新状态
  2. 适当处理错误情况
  3. 保持界面响应性
  4. 提供清晰的用户反馈

希望这篇文章对你实现自定义 Sparkle 更新界面有所帮助!

相关推荐
无限大.5 小时前
前端知识速记:节流与防抖
前端
十八朵郁金香5 小时前
【VUE案例练习】前端vue2+element-ui,后端nodo+express实现‘‘文件上传/删除‘‘功能
前端·javascript·vue.js
学问小小谢5 小时前
第26节课:内容安全策略(CSP)—构建安全网页的防御盾
运维·服务器·前端·网络·学习·安全
LCG元6 小时前
Vue.js组件开发-实现全屏图片文字缩放切换特效
前端·javascript·vue.js
还是鼠鼠7 小时前
图书管理系统 Axios 源码__新增图书
前端·javascript·vscode·ajax·前端框架·node.js·bootstrap
还是鼠鼠10 小时前
图书管理系统 Axios 源码 __删除图书功能
前端·javascript·vscode·ajax·前端框架·node.js·bootstrap
轻口味10 小时前
Vue.js `Suspense` 和异步组件加载
前端·javascript·vue.js
m0_zj11 小时前
8.[前端开发-CSS]Day08-图形-字体-字体图标-元素定位
前端·css
还是鼠鼠11 小时前
图书管理系统 Axios 源码__编辑图书
前端·javascript·vscode·ajax·前端框架
北极象11 小时前
vue3中el-input无法获得焦点的问题
前端·javascript·vue.js