使用 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 更新界面有所帮助!

相关推荐
玩电脑的辣条哥2 小时前
Python如何播放本地音乐并在web页面播放
开发语言·前端·python
ew452182 小时前
ElementUI表格表头自定义添加checkbox,点击选中样式不生效
前端·javascript·elementui
suibian52352 小时前
AI时代:前端开发的职业发展路径拓宽
前端·人工智能
画月的亮2 小时前
element-ui 使用过程中遇到的一些问题及解决方法
javascript·vue.js·ui
Moon.92 小时前
el-table的hasChildren不生效?子级没数据还显示箭头号?树形数据无法展开和收缩
前端·vue.js·html
m0_526119402 小时前
点击el-dialog弹框跳到其他页面浏览器的滚动条消失了多了 el-popup-parent--hidden
javascript·vue.js·elementui
垚垚 Securify 前沿站2 小时前
深入了解 AppScan 工具的使用:筑牢 Web 应用安全防线
运维·前端·网络·安全·web安全·系统安全
工业甲酰苯胺5 小时前
Vue3 基础概念与环境搭建
前端·javascript·vue.js
lyj1689975 小时前
el-tree选中数据重组成树
javascript·vue.js·elementui
打工人你好6 小时前
Swift 的 KeyPath 是什么?
swift