Просмотр исходного кода

Add professional hover lift animation to home action tiles.

This introduces a reusable hoverable tile view and animates icon elevation, shadow depth, and label emphasis so New meeting, Join, and Schedule feel more polished on pointer hover.

Made-with: Cursor
huzaifahayat12 3 месяцев назад
Родитель
Сommit
eeb856b955
1 измененных файлов с 43 добавлено и 2 удалено
  1. 43 2
      zoom_app/ViewController.swift

+ 43 - 2
zoom_app/ViewController.swift

@@ -125,6 +125,34 @@ class ViewController: NSViewController {
         }
         }
     }
     }
 
 
+    private final class HoverTileView: NSView {
+        var onHoverChanged: ((Bool) -> Void)?
+        private var tracking: NSTrackingArea?
+
+        override func updateTrackingAreas() {
+            super.updateTrackingAreas()
+            if let tracking { removeTrackingArea(tracking) }
+            let area = NSTrackingArea(
+                rect: bounds,
+                options: [.activeInActiveApp, .mouseEnteredAndExited, .inVisibleRect],
+                owner: self,
+                userInfo: nil
+            )
+            addTrackingArea(area)
+            tracking = area
+        }
+
+        override func mouseEntered(with event: NSEvent) {
+            super.mouseEntered(with: event)
+            onHoverChanged?(true)
+        }
+
+        override func mouseExited(with event: NSEvent) {
+            super.mouseExited(with: event)
+            onHoverChanged?(false)
+        }
+    }
+
     private var palette = Palette(isDarkMode: true)
     private var palette = Palette(isDarkMode: true)
     private var darkModeEnabled: Bool {
     private var darkModeEnabled: Bool {
         get {
         get {
@@ -3752,7 +3780,7 @@ class ViewController: NSViewController {
     }
     }
 
 
     private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
     private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
-        let root = NSView()
+        let root = HoverTileView()
         root.translatesAutoresizingMaskIntoConstraints = false
         root.translatesAutoresizingMaskIntoConstraints = false
         root.widthAnchor.constraint(equalToConstant: 88).isActive = true
         root.widthAnchor.constraint(equalToConstant: 88).isActive = true
         // Give caption text a little more vertical room so descenders (e.g. "g") are never clipped.
         // Give caption text a little more vertical room so descenders (e.g. "g") are never clipped.
@@ -3779,8 +3807,9 @@ class ViewController: NSViewController {
             $0.translatesAutoresizingMaskIntoConstraints = false
             $0.translatesAutoresizingMaskIntoConstraints = false
             root.addSubview($0)
             root.addSubview($0)
         }
         }
+        let iconTopConstraint = iconButton.topAnchor.constraint(equalTo: root.topAnchor)
         NSLayoutConstraint.activate([
         NSLayoutConstraint.activate([
-            iconButton.topAnchor.constraint(equalTo: root.topAnchor),
+            iconTopConstraint,
             iconButton.centerXAnchor.constraint(equalTo: root.centerXAnchor),
             iconButton.centerXAnchor.constraint(equalTo: root.centerXAnchor),
             iconButton.widthAnchor.constraint(equalToConstant: 50),
             iconButton.widthAnchor.constraint(equalToConstant: 50),
             iconButton.heightAnchor.constraint(equalToConstant: 50),
             iconButton.heightAnchor.constraint(equalToConstant: 50),
@@ -3788,6 +3817,18 @@ class ViewController: NSViewController {
             label.centerXAnchor.constraint(equalTo: root.centerXAnchor),
             label.centerXAnchor.constraint(equalTo: root.centerXAnchor),
             label.bottomAnchor.constraint(equalTo: root.bottomAnchor)
             label.bottomAnchor.constraint(equalTo: root.bottomAnchor)
         ])
         ])
+        root.onHoverChanged = { [weak iconButton, weak label] hovering in
+            NSAnimationContext.runAnimationGroup { context in
+                context.duration = hovering ? 0.16 : 0.14
+                context.timingFunction = CAMediaTimingFunction(name: hovering ? .easeOut : .easeInEaseOut)
+                iconTopConstraint.animator().constant = hovering ? -3 : 0
+                label?.animator().alphaValue = hovering ? 1.0 : 0.96
+            }
+            guard let iconButton else { return }
+            iconButton.layer?.shadowOpacity = hovering ? 0.28 : 0.2
+            iconButton.layer?.shadowRadius = hovering ? 10 : 7
+            iconButton.layer?.shadowOffset = hovering ? NSSize(width: 0, height: -3) : NSSize(width: 0, height: -1)
+        }
         return root
         return root
     }
     }