Change a label on hover

(macOS, Swift, storyboards)


Can I change a label on hover?


WHAT I HAVE TRIED


1- I have put a button in storyboard and make it transparent. It works if I click, but I could nof find how to if hover

@IBOutlet weak var label1: NSTextField!

@IBAction func button1(_ sender: NSButton) {

label1.textColor = NSColor.gray

}


2- I have put a NSView and drag a NSClickGestureRecognizer to it. Again, it works on click but I cannot find a way to do it with hover

@IBOutlet weak var label2: NSTextField!

@IBAction func gestureRecognizer1(_ sender: NSClickGestureRecognizer) {

label2.textColor = NSColor.gray

}


I could find something in Apple documentation. But it talks about UIButton. I cannot find a way to adapt to my case:

https://developer.apple.com/documentation/uikit/uihovergesturerecognizer

Accepted Reply

Please explain exactly what you did, eventually post the code.


- You created a new class definition in XCode, from File menu:

File > New > File

- Select MacOS and Cocoa class

- Subclass of NSTextField

- name LabelWithHelp


Is it how you proceeded ?


You should have got a new file with the following content


import Cocoa

class LabelClass: NSTextField {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // Drawing code here.
    }
   
}


Now replace the content with the following (I have corrected a few errors)


import Cocoa

class LabelWithHelp: NSTextField {
   
    var textForHover = "Some new text"     // This will be changed as needed
    var nonHoverText : String?
   
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        // Drawing code here. If needed
    }
   
    // MARK: - Mouse Events
   
    // --------------------- mouseEntered --------------------------------------------------------------
    //  Description: When hovering over label
    //  Parameters
    //      theEvent: NSEvent
    //  Comments :
    //      Need to have defined trackingArea
    // -------------------------------------------------------------------------------------------------
   
    override func mouseEntered(with theEvent: NSEvent) {
       
        super.mouseEntered(with: theEvent)
        nonHoverText = self.stringValue     // Save the actual content
        self.stringValue = textForHover     // replace in label
    }
   
    // --------------------- mouseExited --------------------------------------------------------------
    //  Description: Exit from hover
    //  Parameters
    //      theEvent: NSEvent
    //  Comments :
    // -------------------------------------------------------------------------------------------------
   
    override func mouseExited(with theEvent: NSEvent) {
        super.mouseExited(with: theEvent)
        self.stringValue = nonHoverText ?? ""     // replace with original text
    }
   }



In the view, in IB, create a label

Change its type to LabelWithHelp


In the ViewController where the label is:

- declare an IBOutlet for the label and a tracking area

    @IBOutlet weak var label: LabelWithHelp!

    fileprivate var labelTrackingArea    : NSTrackingArea?


Connect the IBOutlet to the label in IB


- in viewDidload, set the tracking area:


            labelTrackingArea = NSTrackingArea(rect: label.bounds, options: [NSTrackingArea.Options.mouseEnteredAndExited, NSTrackingArea.Options.cursorUpdate, NSTrackingArea.Options.activeInKeyWindow], owner: label, userInfo: nil)
            label.addTrackingArea(labelTrackingArea!)

Run


You should see the label change when you hover on it and return to its value when exiting.

Replies

uihovergesturerecognizer


is for iOS only ; and does work only for Catalyst.


See this other thread where I detail the solutions for MacOS:

https://forums.developer.apple.com/thread/126227

- trackingArea

or

- tooltip


Changing the label when you hover on it is quite simple with the first option:


In the subclass:


class LabelWithHelp: NSTextField { 

    var textForHover = "Some new text"     // This will be changed as needed
    var nonHoverText : String? 

    override init(frame frameRect: NSRect) {
        super.init(frame: frame)
        self.isEditable = false          // Behave like a label
        self.isSelectable = false
        self.nonHoverText = self.stringValue // The one with which label was created
    }

 
    override func draw(_ dirtyRect: NSRect) { 
        super.draw(dirtyRect) 
        // Drawing code here. If needed 
    } 

    // MARK: - Mouse Events 
   
    // --------------------- mouseEntered -------------------------------------------------------------- 
    //  Description: When hovering over label 
    //  Parameters 
    //      theEvent: NSEvent 
    //  Comments : 
    //      Need to have defined trackingArea 
    // ------------------------------------------------------------------------------------------------- 
   
    override func mouseEntered(with theEvent: NSEvent) { 
       
        super.mouseEntered(with: theEvent) 
        self.stringValue = textForHover
    } 

    // --------------------- mouseExited --------------------------------------------------------------  
    //  Description: Exit from hover  
    //  Parameters  
    //      theEvent: NSEvent  
    //  Comments :  
    // -------------------------------------------------------------------------------------------------  
    
    override func mouseExited(with theEvent: NSEvent) {    
        super.mouseExited(with: theEvent)  
        self.stringValue = nonHoverText        
    }  
    

}

I have created a new document > Cocoa Class and I put your code. Is it the right way? I got several errors. Sorry, probably I do not understand.

Please explain exactly what you did, eventually post the code.


- You created a new class definition in XCode, from File menu:

File > New > File

- Select MacOS and Cocoa class

- Subclass of NSTextField

- name LabelWithHelp


Is it how you proceeded ?


You should have got a new file with the following content


import Cocoa

class LabelClass: NSTextField {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // Drawing code here.
    }
   
}


Now replace the content with the following (I have corrected a few errors)


import Cocoa

class LabelWithHelp: NSTextField {
   
    var textForHover = "Some new text"     // This will be changed as needed
    var nonHoverText : String?
   
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        // Drawing code here. If needed
    }
   
    // MARK: - Mouse Events
   
    // --------------------- mouseEntered --------------------------------------------------------------
    //  Description: When hovering over label
    //  Parameters
    //      theEvent: NSEvent
    //  Comments :
    //      Need to have defined trackingArea
    // -------------------------------------------------------------------------------------------------
   
    override func mouseEntered(with theEvent: NSEvent) {
       
        super.mouseEntered(with: theEvent)
        nonHoverText = self.stringValue     // Save the actual content
        self.stringValue = textForHover     // replace in label
    }
   
    // --------------------- mouseExited --------------------------------------------------------------
    //  Description: Exit from hover
    //  Parameters
    //      theEvent: NSEvent
    //  Comments :
    // -------------------------------------------------------------------------------------------------
   
    override func mouseExited(with theEvent: NSEvent) {
        super.mouseExited(with: theEvent)
        self.stringValue = nonHoverText ?? ""     // replace with original text
    }
   }



In the view, in IB, create a label

Change its type to LabelWithHelp


In the ViewController where the label is:

- declare an IBOutlet for the label and a tracking area

    @IBOutlet weak var label: LabelWithHelp!

    fileprivate var labelTrackingArea    : NSTrackingArea?


Connect the IBOutlet to the label in IB


- in viewDidload, set the tracking area:


            labelTrackingArea = NSTrackingArea(rect: label.bounds, options: [NSTrackingArea.Options.mouseEnteredAndExited, NSTrackingArea.Options.cursorUpdate, NSTrackingArea.Options.activeInKeyWindow], owner: label, userInfo: nil)
            label.addTrackingArea(labelTrackingArea!)

Run


You should see the label change when you hover on it and return to its value when exiting.

Now I understand. It works very well. Thank you!

I work with swift 5.3 and MacOS BigSur 11.0.1.
I used the solution of Claude 31 in my app.
This work very very well.
Question: can I use a TextField control instead a Label ?
If not, how can I add a bound rect to the label ?
@fred7491

Question: can I use a TextField control instead a Label ?

Yes it should work as is for a textField.
Repeat the steps:
  • create a NSTextField in IB

  • Give it LabelWithHelp type

  • Connect to IBOutlet textField

  • Create the trackingRect

Code Block
    fileprivate var textFieldTrackingArea : NSTrackingArea?
  • initialise it in viewDidLoad

Code Block
if textFieldTrackingArea == nil {
textFieldTrackingArea = NSTrackingArea(rect: textField.bounds, options: [NSTrackingArea.Options.mouseEnteredAndExited, NSTrackingArea.Options.cursorUpdate, NSTrackingArea.Options.activeInKeyWindow], owner: textField, userInfo: nil)
textField.addTrackingArea(textFieldTrackingArea!)
}

Don't forget to set the width and height of the TextField in IB, otherwise it may not show.

how can I add a bound rect to the label ?

Select the label on the IB canvas.
Open the Attributes Inspector
Set the Border property as you wish.