How to add animated icon to OS X status bar?

前端 未结 3 1093
你的背包
你的背包 2021-01-30 02:34

I want to put an icon in Mac OS status bar as part of my cocoa application. What I do right now is:

NSStatusBar *bar = [NSStatusBar systemStatusBar];

sbItem =          


        
相关标签:
3条回答
  • 2021-01-30 03:17

    I re-wrote Rob's solution so that I can reuse it:

    I have number of frames 9 and all the images name has last digit as frame number so that I can reset the image each time to animate the icon.

    //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
    

    How to use:

    1. Conform to protocol in your StatusMenu class

      //create IntervalAnimator object
      
      animator = [[IntervalAnimator alloc] init];
      
      [animator setDelegate:self];
      
      [animator setNumberOfFrames:9];
      
      [animator startAnimating];
      
    2. Implement protocol method

      -(void)onUpdate    {
      
          [animator setCurrentFrame:([animator currentFrame] + 1)%[animator numberOfFrames]];
      
          NSImage* image = [NSImage imageNamed:[NSString stringWithFormat:@"icon%ld", (long)[animator currentFrame]]];
      
          [statusItem setImage:image];
      
      }
      
    0 讨论(0)
  • 2021-01-30 03:20

    Just had to do something similar recently in a simple project, so I'm posting my personal version written in 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
            }
        }
    }
    

    Usage:

    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()
    }
    
    0 讨论(0)
  • 2021-01-30 03:24

    You'll need to repeatedly call -setImage: on your NSStatusItem, passing in a different image each time. The easiest way to do this would be with an NSTimer and an instance variable to store the current frame of the animation.

    Something like this:

    /*
    
    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题