@@ -9,6 +9,12 @@ import AppKit
99
1010extension FindViewController {
1111 /// Show the find panel
12+ ///
13+ /// Performs the following:
14+ /// - Makes the find panel the first responder.
15+ /// - Sets the find panel to be just outside the visible area (`resolvedTopPadding - FindPanel.height`).
16+ /// - Animates the find panel into position (resolvedTopPadding).
17+ /// - Makes the find panel the first responder.
1218 func showFindPanel( animated: Bool = true ) {
1319 if isShowingFindPanel {
1420 // If panel is already showing, just focus the text field
@@ -18,46 +24,40 @@ extension FindViewController {
1824
1925 isShowingFindPanel = true
2026
21- let updates : ( ) -> Void = { [ self ] in
27+ // Smooth out the animation by placing the find panel just outside the correct position before animating.
28+ findPanel. isHidden = false
29+ findPanelVerticalConstraint. constant = resolvedTopPadding - FindPanel. height
30+ view. layoutSubtreeIfNeeded ( )
31+
32+ // Perform the animation
33+ conditionalAnimated ( animated) {
2234 // SwiftUI breaks things here, and refuses to return the correct `findPanel.fittingSize` so we
2335 // are forced to use a constant number.
2436 target? . findPanelWillShow ( panelHeight: FindPanel . height)
2537 setFindPanelConstraintShow ( )
26- }
27-
28- // Smooth this animation
29- findPanel. isHidden = false
30- findPanelVerticalConstraint. constant = topPadding - FindPanel. height
31- view. layoutSubtreeIfNeeded ( )
32-
33- if animated {
34- withAnimation ( updates) { }
35- } else {
36- updates ( )
37- }
38+ } onComplete: { }
3839
3940 _ = findPanel? . becomeFirstResponder ( )
4041 findPanel? . addEventMonitor ( )
4142 }
4243
4344 /// Hide the find panel
45+ ///
46+ /// Performs the following:
47+ /// - Resigns the find panel from first responder.
48+ /// - Animates the find panel just outside the visible area (`resolvedTopPadding - FindPanel.height`).
49+ /// - Hides the find panel.
50+ /// - Sets the text view to be the first responder.
4451 func hideFindPanel( animated: Bool = true ) {
4552 isShowingFindPanel = false
4653 _ = findPanel? . resignFirstResponder ( )
4754 findPanel? . removeEventMonitor ( )
4855
49- let updates : ( ) -> Void = { [ self ] in
56+ conditionalAnimated ( animated ) {
5057 target? . findPanelWillHide ( panelHeight: FindPanel . height)
5158 setFindPanelConstraintHide ( )
52- }
53-
54- if animated {
55- withAnimation ( updates) { [ weak self] in
56- self ? . findPanel. isHidden = true
57- }
58- } else {
59- updates ( )
60- findPanel. isHidden = true
59+ } onComplete: { [ weak self] in
60+ self ? . findPanel. isHidden = true
6161 }
6262
6363 // Set first responder back to text view
@@ -66,12 +66,26 @@ extension FindViewController {
6666 }
6767 }
6868
69+ /// Performs an animation with a completion handler, conditionally animating the changes.
70+ /// - Parameters:
71+ /// - animated: Determines if the changes are performed in an animation context.
72+ /// - animatable: Perform the changes to be animated in this callback. Implicit animation will be enabled.
73+ /// - onComplete: Called when the changes are complete, animated or not.
74+ private func conditionalAnimated( _ animated: Bool , animatable: ( ) -> Void , onComplete: @escaping ( ) -> Void ) {
75+ if animated {
76+ withAnimation ( animatable, onComplete: onComplete)
77+ } else {
78+ animatable ( )
79+ onComplete ( )
80+ }
81+ }
82+
6983 /// Runs the `animatable` callback in an animation context with implicit animation enabled.
7084 /// - Parameter animatable: The callback run in the animation context. Perform layout or view updates in this
7185 /// callback to have them animated.
7286 private func withAnimation( _ animatable: ( ) -> Void , onComplete: @escaping ( ) -> Void ) {
7387 NSAnimationContext . runAnimationGroup { animator in
74- animator. duration = 0.2
88+ animator. duration = 0.15
7589 animator. allowsImplicitAnimation = true
7690
7791 animatable ( )
@@ -87,7 +101,7 @@ extension FindViewController {
87101 /// Can be animated using implicit animation.
88102 func setFindPanelConstraintShow( ) {
89103 // Update the find panel's top to be equal to the view's top.
90- findPanelVerticalConstraint. constant = topPadding
104+ findPanelVerticalConstraint. constant = resolvedTopPadding
91105 findPanelVerticalConstraint. isActive = true
92106 }
93107
@@ -99,7 +113,7 @@ extension FindViewController {
99113 // SwiftUI hates us. It refuses to move views outside of the safe are if they don't have the `.ignoresSafeArea`
100114 // modifier, but with that modifier on it refuses to allow it to be animated outside the safe area.
101115 // The only way I found to fix it was to multiply the height by 3 here.
102- findPanelVerticalConstraint. constant = topPadding - FindPanel. height
116+ findPanelVerticalConstraint. constant = resolvedTopPadding - ( FindPanel . height * 3 )
103117 findPanelVerticalConstraint. isActive = true
104118 }
105119}
0 commit comments