🐰

NSWindowのズームアニメーションを変更する小技

に公開

ウインドウの「拡大/縮小」(英語では“Zoom”)または信号機ボタンの緑色プラスボタン(Optionキーを押すとフルスクリーンボタンがプラスボタンに変化)を押した時のズームアニメーションを変更するには、次のようにします。

アニメーションの秒数を変更

animationResizeTime(_:)で新フレームと現フレームの差分から適度な秒数を算出します。
デフォルト実装では UserDefaults の NSWindowResizeTime (=0.20) を使用します。この値は150px分がリサイズに要する時間としてシステムで定義されているようです。

https://developer.apple.com/documentation/appkit/nswindow/animationresizetime(_:)

import Cocoa

class CustomWindow: NSWindow {
	override func animationResizeTime(_ newFrame: NSRect) -> TimeInterval {
		3.0
	}
}

CoreAnimationを使ってバウンス効果付きに変更/イージング関数を変更

import Cocoa

class CustomWindow: NSWindow {
	override func setFrame(_ frameRect: NSRect, display displayFlag: Bool, animate animateFlag: Bool) {
		if !animateFlag || NSWorkspace.shared.accessibilityDisplayShouldReduceMotion {
			// アニメーションなし
			setFrame(frameRect, display: displayFlag)
		}
		else {
			let anim = CASpringAnimation(perceptualDuration: 1.0, bounce: 0.5)
			anim.timingFunction = CAMediaTimingFunction.easeOutQuint()
			animations = ["frame" : anim]
			animator().setFrame(frameRect, display: displayFlag)
		}
	}
}
import QuartzCore

extension CAMediaTimingFunction {
	static func easeOutQuint() -> CAMediaTimingFunction {
		// https://easings.net/ja#easeOutQuint
		return CAMediaTimingFunction(controlPoints: 0.22, 1, 0.36, 1)
	}
}

NSAnimationContextを使って秒数とイージング関数を変更

import Cocoa

class CustomWindow: NSWindow {
	override func setFrame(_ frameRect: NSRect, display displayFlag: Bool, animate animateFlag: Bool) {
		if !animateFlag || NSWorkspace.shared.accessibilityDisplayShouldReduceMotion {
			// アニメーションなし
			setFrame(frameRect, display: displayFlag)
		}
		else {
			NSAnimationContext.runAnimationGroup { ctx in
				ctx.timingFunction = CAMediaTimingFunction.easeOutQuint()
				ctx.allowsImplicitAnimation = true
				ctx.duration = animationResizeTime(frameRect)
				
				// super の `animate` 付きメソッドを使うとアニメーションが一瞬固まる現象があるので、`animate`なし版を使用すること
				setFrame(frameRect, display: displayFlag)
			}
		}
	}
}

補足1:アクセシビリティの「視差効果を減らす」に対応する

NSWorkspace.shared.accessibilityDisplayShouldReduceMotionでBool値が取れるので、これがtrueの時にはアニメーションを無効化するか、ディゾルブなどの視覚刺激が少ないアニメーションに変更しましょう。

https://developer.apple.com/documentation/appkit/nsworkspace/accessibilitydisplayshouldreducemotion

https://qiita.com/usagimaru/items/4c0dc68659bebb3521f8

補足2:NSWindowResizeTimeはコマンドライン引数やUserDefaultsとしてもセットできる

どちらかというとこの方法の方が有名ですが、コマンドライン引数(Launch Arguments)で渡したり、defaultsコマンドでUserDefaultsに書き込む値としてもセットできます。

## ウインドウリサイズのアニメーションを実質的に無効化する
defaults write <DOMAIN> NSWindowResizeTime -float 0.001

https://blog.kishikawakatsumi.com/entry/20140117/1389929450

Discussion