如何将动画图标添加到 OS X 状态栏?
Posted
技术标签:
【中文标题】如何将动画图标添加到 OS X 状态栏?【英文标题】:How to add animated icon to OS X status bar? 【发布时间】:2011-09-30 12:19:19 【问题描述】:我想在 Mac OS 状态栏中放置一个图标,作为我的 cocoa 应用程序的一部分。我现在做的是:
NSStatusBar *bar = [NSStatusBar systemStatusBar];
sbItem = [bar statusItemWithLength:NSVariableStatusItemLength];
[sbItem retain];
[sbItem setImage:[NSImage imageNamed:@"Taski_bar_icon.png"]];
[sbItem setHighlightMode:YES];
[sbItem setAction:@selector(stopStart)];
但如果我希望图标具有动画效果(3-4 帧),我该怎么做?
【问题讨论】:
好吧,我想给人一种应用程序正在处理数据的印象——这种情况很少发生,但很高兴知道。还是不应该? 这是一个有效的用途。哎呀,时间机器做到了。 我认为 Dropbox 处理得很好。这很微妙,但它告诉你事情正在更新。 Time Machine 使用来做到这一点。在 macOS 10.15 中不会发生这种情况。 【参考方案1】:您需要在NSStatusItem
上反复调用-setImage:
,每次都传入不同的图像。最简单的方法是使用NSTimer
和一个实例变量来存储动画的当前帧。
类似这样的:
/*
assume these instance variables are defined:
NSInteger currentFrame;
NSTimer* animTimer;
*/
- (void)startAnimating
currentFrame = 0;
animTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0 target:self selector:@selector(updateImage:) userInfo:nil repeats:YES];
- (void)stopAnimating
[animTimer invalidate];
- (void)updateImage:(NSTimer*)timer
//get the image for the current frame
NSImage* image = [NSImage imageNamed:[NSString stringWithFormat:@"image%d",currentFrame]];
[statusBarItem setImage:image];
currentFrame++;
if (currentFrame % 4 == 0)
currentFrame = 0;
【讨论】:
是否也可以将 NSView 或 NSImageView 放在状态栏项目上?怎么办? 你必须告诉 animTimer 通过 [animTimer fire] 触发还是自动触发?+scheduledTimerWithTimeInterval:...
方法将为您将计时器添加到运行循环(即scheduled
位),因此您无需自己触发计时器。
- (void)updateImage:(NSTimer*)timer add currentFrame++;
谢谢,我添加了计数器递增。【参考方案2】:
我重写了 Rob 的解决方案,以便我可以重复使用它:
我的帧数为 9,所有图像名称的最后一位都是帧号,这样我每次都可以重置图像以动画图标。
//IntervalAnimator.h
#import <Foundation/Foundation.h>
@protocol IntervalAnimatorDelegate <NSObject>
- (void)onUpdate;
@end
@interface IntervalAnimator : NSObject
NSInteger numberOfFrames;
NSInteger currentFrame;
__unsafe_unretained id <IntervalAnimatorDelegate> delegate;
@property(assign) id <IntervalAnimatorDelegate> delegate;
@property (nonatomic) NSInteger numberOfFrames;
@property (nonatomic) NSInteger currentFrame;
- (void)startAnimating;
- (void)stopAnimating;
@end
#import "IntervalAnimator.h"
@interface IntervalAnimator()
NSTimer* animTimer;
@end
@implementation IntervalAnimator
@synthesize numberOfFrames;
@synthesize delegate;
@synthesize currentFrame;
- (void)startAnimating
currentFrame = -1;
animTimer = [NSTimer scheduledTimerWithTimeInterval:0.50 target:delegate selector:@selector(onUpdate) userInfo:nil repeats:YES];
- (void)stopAnimating
[animTimer invalidate];
@end
使用方法:
遵守 StatusMenu 类中的协议
//create IntervalAnimator object
animator = [[IntervalAnimator alloc] init];
[animator setDelegate:self];
[animator setNumberOfFrames:9];
[animator startAnimating];
实现协议方法
-(void)onUpdate
[animator setCurrentFrame:([animator currentFrame] + 1)%[animator numberOfFrames]];
NSImage* image = [NSImage imageNamed:[NSString stringWithFormat:@"icon%ld", (long)[animator currentFrame]]];
[statusItem setImage:image];
【讨论】:
【参考方案3】:最近在一个简单的项目中不得不做类似的事情,所以我发布了我用 Swift 编写的个人版本:
class StatusBarIconAnimationUtils: NSObject
private var currentFrame = 0
private var animTimer : Timer
private var statusBarItem: NSStatusItem!
private var imageNamePattern: String!
private var imageCount : Int!
init(statusBarItem: NSStatusItem!, imageNamePattern: String, imageCount: Int)
self.animTimer = Timer.init()
self.statusBarItem = statusBarItem
self.imageNamePattern = imageNamePattern
self.imageCount = imageCount
super.init()
func startAnimating()
stopAnimating()
currentFrame = 0
animTimer = Timer.scheduledTimer(timeInterval: 5.0 / 30.0, target: self, selector: #selector(self.updateImage(_:)), userInfo: nil, repeats: true)
func stopAnimating()
animTimer.invalidate()
setImage(frameCount: 0)
@objc private func updateImage(_ timer: Timer?)
setImage(frameCount: currentFrame)
currentFrame += 1
if currentFrame % imageCount == 0
currentFrame = 0
private func setImage(frameCount: Int)
let imagePath = "\(imageNamePattern!)\(frameCount)"
print("Switching image to: \(imagePath)")
let image = NSImage(named: NSImage.Name(imagePath))
image?.isTemplate = true // best for dark mode
DispatchQueue.main.async
self.statusBarItem.button?.image = image
用法:
private let statusItem =
NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
// Assuming we have a set of images with names: statusAnimatedIcon0, ..., statusAnimatedIcon6
private lazy var iconAnimation =
StatusBarIconAnimationUtils.init(statusBarItem: statusItem, imageNamePattern: "statusAnimatedIcon",
imageCount: 7)
private func startAnimation()
iconAnimation.startAnimating()
private func stopAnimating()
iconAnimation.stopAnimating()
【讨论】:
以上是关于如何将动画图标添加到 OS X 状态栏?的主要内容,如果未能解决你的问题,请参考以下文章