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

相关推荐
想要学好前端的阿毛1 分钟前
React状态管理库 -- Redux篇
前端
拾光拾趣录2 分钟前
前端宏(微)任务 | 从“为什么我的代码不按顺序执行”说起
前端·javascript
林太白5 分钟前
NestJS-菜单模块
前端·后端·nestjs
程序员ys7 分钟前
微前端(What)
前端·架构
狗都不学爬虫_8 分钟前
JS逆向 - (国外)SHEIN站 - 请求头(armorToken、Anti-in)
javascript·python·ajax·网络爬虫·wasm
用户797476112739 分钟前
Mysql RR事务隔离级别引发的生产Bug,你中招了吗?
前端
Mintopia11 分钟前
🧠 三分视界:Three.js 离屏渲染与多重视角的艺术
前端·javascript·计算机图形学
JarvanMo20 分钟前
Dart & Flutter DevTools 扩展
前端
yuko093122 分钟前
【手机验证码】手机号格式化光标异常问题
前端
原生高钙23 分钟前
高性能前端埋点上报系统的架构与实现
前端·面试